[automerger skipped] Merge "Merge "Remove LocaleListTest#testRepeatedArguments" into oreo-mr1-cts-dev am: deec1492ed" into pie-cts-dev am: 1fc8bf4049 -s ours am: f91456a253 am: 00a1d12f7f
Original change: https://android-review.googlesource.com/c/platform/cts/+/1437896
Change-Id: I3c701ccd965a43a402baa51e731161095e70d05d
diff --git a/hostsidetests/backup/OWNERS b/hostsidetests/backup/OWNERS
index 3637e32..c28c4d8 100644
--- a/hostsidetests/backup/OWNERS
+++ b/hostsidetests/backup/OWNERS
@@ -1,3 +1,4 @@
+# Bug component: 41666
# Use this reviewer by default.
br-framework-team+reviews@google.com
diff --git a/hostsidetests/backup/RestoreSessionTest/Android.bp b/hostsidetests/backup/RestoreSessionTest/Android.bp
new file mode 100644
index 0000000..a3ac883
--- /dev/null
+++ b/hostsidetests/backup/RestoreSessionTest/Android.bp
@@ -0,0 +1,33 @@
+// 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.
+
+android_test_helper_app {
+ name: "CtsRestoreSessionApp",
+ defaults: ["cts_defaults"],
+ static_libs: [
+ "androidx.test.rules",
+ "platform-test-annotations",
+ "truth-prebuilt",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ // tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+ sdk_version: "system_current",
+}
diff --git a/hostsidetests/backup/RestoreSessionTest/AndroidManifest.xml b/hostsidetests/backup/RestoreSessionTest/AndroidManifest.xml
new file mode 100644
index 0000000..130e3aa
--- /dev/null
+++ b/hostsidetests/backup/RestoreSessionTest/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 Google Inc.
+ *
+ * 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 Licensea
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.cts.backup.restoresessionapp">
+
+ <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
+
+ <application
+ android:label="RestoreSessionApp"
+ android:allowBackup="true">
+
+ <uses-library android:name="android.test.runner" />
+
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.cts.backup.restoresessionapp" />
+</manifest>
diff --git a/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/BaseRestoreSessionAppTest.java b/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/BaseRestoreSessionAppTest.java
new file mode 100644
index 0000000..85b05d3
--- /dev/null
+++ b/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/BaseRestoreSessionAppTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * 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.cts.backup.restoresessionapp;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class BaseRestoreSessionAppTest {
+ private static final String SHARED_PREFERENCES_FILE = "restore_session_app_prefs";
+
+ private SharedPreferences mPreferences;
+
+ @Before
+ public void setUp() {
+ mPreferences =
+ getTargetContext()
+ .getSharedPreferences(SHARED_PREFERENCES_FILE, Context.MODE_PRIVATE);
+ }
+
+ protected void clearSharedPrefs() {
+ mPreferences.edit().clear().commit();
+ }
+
+ protected void checkSharedPrefsDontExist(String prefKey) {
+ assertThat(mPreferences.getInt(prefKey, 0)).isEqualTo(0);
+ }
+
+ protected void saveValuesToSharedPrefs(String prefKey, int prefValue) {
+ mPreferences.edit().putInt(prefKey, prefValue).commit();
+ }
+
+ protected void checkSharedPrefsExist(String prefKey, int prefValue) {
+ assertThat(mPreferences.getInt(prefKey, 0)).isEqualTo(prefValue);
+ }
+}
diff --git a/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/RestoreSessionTest.java b/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/RestoreSessionTest.java
new file mode 100644
index 0000000..06b9ae0
--- /dev/null
+++ b/hostsidetests/backup/RestoreSessionTest/src/android/cts/backup/restoresessionapp/RestoreSessionTest.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * 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.cts.backup.restoresessionapp;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertNotEquals;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.app.backup.BackupManager;
+import android.app.backup.BackupManagerMonitor;
+import android.app.backup.RestoreObserver;
+import android.app.backup.RestoreSession;
+import android.app.backup.RestoreSet;
+import android.content.Context;
+import android.os.Bundle;
+
+import android.platform.test.annotations.AppModeFull;
+import androidx.test.runner.AndroidJUnit4;
+
+// import com.android.compatibility.common.util.SystemUtil;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Device side routines to be invoked by the host side RestoreSessionHostSideTest. These are not
+ * designed to be called in any other way, as they rely on state set up by the host side test.
+ */
+@RunWith(AndroidJUnit4.class)
+@AppModeFull
+public class RestoreSessionTest {
+ private static final String PACKAGE_1 = "android.cts.backup.restoresessionapp1";
+ private static final String PACKAGE_2 = "android.cts.backup.restoresessionapp2";
+ private static final String PACKAGE_3 = "android.cts.backup.restoresessionapp3";
+
+ private static final int RESTORE_TIMEOUT_SECONDS = 10;
+
+ private BackupManager mBackupManager;
+ private Set<String> mRestorePackages;
+ private Set<String> mNonRestorePackages;
+ private CountDownLatch mRestoreObserverLatch;
+ private RestoreSession mRestoreSession;
+ private UiAutomation mUiAutomation;
+ private long mRestoreToken;
+
+ private final RestoreObserver mRestoreObserver =
+ new RestoreObserver() {
+ @Override
+ public void restoreSetsAvailable(RestoreSet[] result) {
+ super.restoreSetsAvailable(result);
+
+ long token = 0L;
+
+ for (RestoreSet restoreSet : result) {
+ long restoreToken = restoreSet.token;
+ if (doesRestoreSetContainAllPackages(restoreToken, mRestorePackages)
+ && doesRestoreSetContainAllPackages(
+ restoreToken, mNonRestorePackages)) {
+ token = restoreSet.token;
+ break;
+ }
+ }
+
+ mRestoreToken = token;
+
+ mRestoreObserverLatch.countDown();
+ }
+
+ @Override
+ public void restoreStarting(int numPackages) {
+ super.restoreStarting(numPackages);
+
+ assertEquals(
+ "Wrong number of packages in the restore set",
+ mRestorePackages.size(),
+ numPackages);
+ mRestoreObserverLatch.countDown();
+ }
+
+ @Override
+ public void onUpdate(int nowBeingRestored, String currentPackage) {
+ super.onUpdate(nowBeingRestored, currentPackage);
+
+ assertTrue(
+ "Restoring package that is not in mRestorePackages",
+ mRestorePackages.contains(currentPackage));
+ mRestoreObserverLatch.countDown();
+ }
+
+ @Override
+ public void restoreFinished(int error) {
+ super.restoreFinished(error);
+
+ assertEquals(
+ "Restore finished with error: " + error, BackupManager.SUCCESS, error);
+ mRestoreSession.endRestoreSession();
+ mRestoreObserverLatch.countDown();
+ }
+ };
+
+ @Before
+ public void setUp() throws InterruptedException {
+ Context context = getTargetContext();
+ mBackupManager = new BackupManager(context);
+
+ mRestorePackages = new HashSet<>();
+ mRestorePackages.add(PACKAGE_1);
+ mRestorePackages.add(PACKAGE_2);
+
+ mNonRestorePackages = new HashSet<>();
+ mNonRestorePackages.add(PACKAGE_3);
+
+ mRestoreToken = 0L;
+
+ mUiAutomation = getInstrumentation().getUiAutomation();
+ mUiAutomation.adoptShellPermissionIdentity();
+
+ loadAvailableRestoreSets();
+ }
+
+ @After
+ public void tearDown() {
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+
+ /**
+ * Restore packages added to mRestorePackages and verify only those packages are restored. Use
+ * {@link RestoreSession#restorePackages(long, RestoreObserver, Set)}
+ */
+ @Test
+ public void testRestorePackages() throws InterruptedException {
+ testRestorePackagesInternal(false);
+ }
+
+ /**
+ * Restore packages added to mRestorePackages and verify only those packages are restored. Use
+ * {@link RestoreSession#restorePackages(long, RestoreObserver, Set, BackupManagerMonitor)}
+ */
+ @Test
+ public void testRestorePackagesWithMonitorParam() throws InterruptedException {
+ testRestorePackagesInternal(true);
+ }
+
+ private void testRestorePackagesInternal(boolean useMonitorParam) throws InterruptedException {
+ // Wait for the callbacks from RestoreObserver: one for each package from
+ // mRestorePackages plus restoreStarting and restoreFinished.
+ mRestoreObserverLatch = new CountDownLatch(mRestorePackages.size() + 2);
+ CountDownLatch backupMonitorLatch = null;
+ if (useMonitorParam) {
+ // Wait for the callbacks from BackupManagerMonitor: one for each package.
+ backupMonitorLatch = new CountDownLatch(mRestorePackages.size());
+ mRestoreSession.restorePackages(
+ mRestoreToken,
+ mRestoreObserver,
+ mRestorePackages,
+ new TestBackupMonitor(backupMonitorLatch));
+ } else {
+ mRestoreSession.restorePackages(mRestoreToken, mRestoreObserver, mRestorePackages);
+ }
+
+ awaitResultAndAssertSuccess(mRestoreObserverLatch);
+ if (backupMonitorLatch != null) {
+ awaitResultAndAssertSuccess(backupMonitorLatch);
+ }
+ }
+
+ private void loadAvailableRestoreSets() throws InterruptedException {
+ // Wait for getAvailableRestoreSets to finish and the callback to be fired.
+ mRestoreObserverLatch = new CountDownLatch(1);
+ mRestoreSession = mBackupManager.beginRestoreSession();
+ assertEquals(
+ BackupManager.SUCCESS, mRestoreSession.getAvailableRestoreSets(mRestoreObserver));
+ awaitResultAndAssertSuccess(mRestoreObserverLatch);
+
+ assertNotEquals("Restore set not found", 0L, mRestoreToken);
+ }
+
+ private boolean doesRestoreSetContainAllPackages(long restoreToken, Set<String> packages) {
+ for (String restorePackage : packages) {
+ if (mBackupManager.getAvailableRestoreToken(restorePackage) != restoreToken) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void awaitResultAndAssertSuccess(CountDownLatch latch) throws InterruptedException {
+ boolean waitResult = latch.await(RESTORE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertTrue("Restore timed out", waitResult);
+ }
+
+ private static class TestBackupMonitor extends BackupManagerMonitor {
+ private final CountDownLatch mLatch;
+
+ TestBackupMonitor(CountDownLatch latch) {
+ mLatch = latch;
+ }
+
+ @Override
+ public void onEvent(Bundle event) {
+ super.onEvent(event);
+
+ int eventType = event.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID);
+ assertEquals(
+ "Unexpected event from BackupManagerMonitor: " + eventType,
+ BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH,
+ eventType);
+ mLatch.countDown();
+ }
+ }
+}
diff --git a/hostsidetests/backup/restoresessionapp1/Android.bp b/hostsidetests/backup/restoresessionapp1/Android.bp
new file mode 100644
index 0000000..2eb5d0e
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp1/Android.bp
@@ -0,0 +1,32 @@
+// 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.
+
+android_test_helper_app {
+ name: "CtsRestoreSessionApp1",
+ defaults: ["cts_defaults"],
+ static_libs: [
+ "androidx.test.rules",
+ "truth-prebuilt",
+ "CtsRestoreSessionApp",
+ ],
+ srcs: [
+ "src/**/*.java"
+ ],
+ // tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ ],
+ sdk_version: "system_current",
+}
diff --git a/hostsidetests/backup/restoresessionapp1/AndroidManifest.xml b/hostsidetests/backup/restoresessionapp1/AndroidManifest.xml
new file mode 100644
index 0000000..ae6b205
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp1/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 Google Inc.
+ *
+ * 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 Licensea
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.cts.backup.restoresessionapp1">
+
+ <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
+
+ <application
+ android:label="RestoreSessionApp"
+ android:allowBackup="true">
+
+ <uses-library android:name="android.test.runner" />
+
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.cts.backup.restoresessionapp1" />
+</manifest>
diff --git a/hostsidetests/backup/restoresessionapp1/src/android/cts/backup/restoresessionapp1/RestoreSessionAppTest.java b/hostsidetests/backup/restoresessionapp1/src/android/cts/backup/restoresessionapp1/RestoreSessionAppTest.java
new file mode 100644
index 0000000..f82d058
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp1/src/android/cts/backup/restoresessionapp1/RestoreSessionAppTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * 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.cts.backup.restoresessionapp1;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import android.cts.backup.restoresessionapp.BaseRestoreSessionAppTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Device side routines to be invoked by the host side RestoreSessionHostSideTest. These are not
+ * designed to be called in any other way, as they rely on state set up by the host side test.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RestoreSessionAppTest extends BaseRestoreSessionAppTest {
+ private static final String SHARED_PREFERENCES_KEY = "test_key_1";
+ private static final int SHARED_PREFERENCES_VALUE = 123;
+
+ @Test
+ public void testClearSharedPrefs() {
+ clearSharedPrefs();
+ }
+
+ @Test
+ public void testCheckSharedPrefsDontExist() {
+ checkSharedPrefsDontExist(SHARED_PREFERENCES_KEY);
+ }
+
+ @Test
+ public void testSaveValuesToSharedPrefs() {
+ saveValuesToSharedPrefs(SHARED_PREFERENCES_KEY, SHARED_PREFERENCES_VALUE);
+ }
+
+ @Test
+ public void testCheckSharedPrefsExist() {
+ checkSharedPrefsExist(SHARED_PREFERENCES_KEY, SHARED_PREFERENCES_VALUE);
+ }
+}
diff --git a/hostsidetests/backup/restoresessionapp2/Android.bp b/hostsidetests/backup/restoresessionapp2/Android.bp
new file mode 100644
index 0000000..e389054
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp2/Android.bp
@@ -0,0 +1,32 @@
+// 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.
+
+android_test_helper_app {
+ name: "CtsRestoreSessionApp2",
+ defaults: ["cts_defaults"],
+ static_libs: [
+ "androidx.test.rules",
+ "truth-prebuilt",
+ "CtsRestoreSessionApp",
+ ],
+ srcs: [
+ "src/**/*.java"
+ ],
+ // tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ ],
+ sdk_version: "system_current",
+}
diff --git a/hostsidetests/backup/restoresessionapp2/AndroidManifest.xml b/hostsidetests/backup/restoresessionapp2/AndroidManifest.xml
new file mode 100644
index 0000000..757c801
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp2/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 Google Inc.
+ *
+ * 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 Licensea
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.cts.backup.restoresessionapp2">
+
+ <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
+
+ <application
+ android:label="RestoreSessionApp"
+ android:allowBackup="true"/>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.cts.backup.restoresessionapp2" />
+</manifest>
diff --git a/hostsidetests/backup/restoresessionapp2/src/android/cts/backup/restoresessionapp2/RestoreSessionAppTest.java b/hostsidetests/backup/restoresessionapp2/src/android/cts/backup/restoresessionapp2/RestoreSessionAppTest.java
new file mode 100644
index 0000000..458103b
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp2/src/android/cts/backup/restoresessionapp2/RestoreSessionAppTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * 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.cts.backup.restoresessionapp2;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import android.cts.backup.restoresessionapp.BaseRestoreSessionAppTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Device side routines to be invoked by the host side RestoreSessionHostSideTest. These are not
+ * designed to be called in any other way, as they rely on state set up by the host side test.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RestoreSessionAppTest extends BaseRestoreSessionAppTest {
+ private static final String SHARED_PREFERENCES_KEY = "test_key_2";
+ private static final int SHARED_PREFERENCES_VALUE = 124;
+
+ @Test
+ public void testClearSharedPrefs() {
+ clearSharedPrefs();
+ }
+
+ @Test
+ public void testCheckSharedPrefsDontExist() {
+ checkSharedPrefsDontExist(SHARED_PREFERENCES_KEY);
+ }
+
+ @Test
+ public void testSaveValuesToSharedPrefs() {
+ saveValuesToSharedPrefs(SHARED_PREFERENCES_KEY, SHARED_PREFERENCES_VALUE);
+ }
+
+ @Test
+ public void testCheckSharedPrefsExist() {
+ checkSharedPrefsExist(SHARED_PREFERENCES_KEY, SHARED_PREFERENCES_VALUE);
+ }
+}
diff --git a/hostsidetests/backup/restoresessionapp3/Android.bp b/hostsidetests/backup/restoresessionapp3/Android.bp
new file mode 100644
index 0000000..1451823
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp3/Android.bp
@@ -0,0 +1,32 @@
+// 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.
+
+android_test_helper_app {
+ name: "CtsRestoreSessionApp3",
+ defaults: ["cts_defaults"],
+ static_libs: [
+ "androidx.test.rules",
+ "truth-prebuilt",
+ "CtsRestoreSessionApp",
+ ],
+ srcs: [
+ "src/**/*.java"
+ ],
+ // tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ ],
+ sdk_version: "system_current",
+}
diff --git a/hostsidetests/backup/restoresessionapp3/AndroidManifest.xml b/hostsidetests/backup/restoresessionapp3/AndroidManifest.xml
new file mode 100644
index 0000000..027cf68
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp3/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 Google Inc.
+ *
+ * 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 Licensea
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.cts.backup.restoresessionapp3">
+
+ <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26"/>
+
+ <application
+ android:label="RestoreSessionApp"
+ android:allowBackup="true"/>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.cts.backup.restoresessionapp3" />
+</manifest>
diff --git a/hostsidetests/backup/restoresessionapp3/src/android/cts/backup/restoresessionapp3/RestoreSessionAppTest.java b/hostsidetests/backup/restoresessionapp3/src/android/cts/backup/restoresessionapp3/RestoreSessionAppTest.java
new file mode 100644
index 0000000..b034e1a
--- /dev/null
+++ b/hostsidetests/backup/restoresessionapp3/src/android/cts/backup/restoresessionapp3/RestoreSessionAppTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 Google Inc.
+ *
+ * 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.cts.backup.restoresessionapp3;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import android.cts.backup.restoresessionapp.BaseRestoreSessionAppTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Device side routines to be invoked by the host side RestoreSessionHostSideTest. These are not
+ * designed to be called in any other way, as they rely on state set up by the host side test.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RestoreSessionAppTest extends BaseRestoreSessionAppTest {
+ private static final String SHARED_PREFERENCES_KEY = "test_key_3";
+ private static final int SHARED_PREFERENCES_VALUE = 125;
+
+ @Test
+ public void testClearSharedPrefs() {
+ clearSharedPrefs();
+ }
+
+ @Test
+ public void testCheckSharedPrefsDontExist() {
+ checkSharedPrefsDontExist(SHARED_PREFERENCES_KEY);
+ }
+
+ @Test
+ public void testSaveValuesToSharedPrefs() {
+ saveValuesToSharedPrefs(SHARED_PREFERENCES_KEY, SHARED_PREFERENCES_VALUE);
+ }
+
+ @Test
+ public void testCheckSharedPrefsExist() {
+ checkSharedPrefsExist(SHARED_PREFERENCES_KEY, SHARED_PREFERENCES_VALUE);
+ }
+}
diff --git a/hostsidetests/backup/src/android/cts/backup/RestoreSessionHostSideTest.java b/hostsidetests/backup/src/android/cts/backup/RestoreSessionHostSideTest.java
new file mode 100644
index 0000000..9c5d890
--- /dev/null
+++ b/hostsidetests/backup/src/android/cts/backup/RestoreSessionHostSideTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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 android.cts.backup;
+
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.AppModeFull;
+
+import com.android.compatibility.common.util.BackupUtils;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import java.io.IOException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Optional;
+
+/**
+ * Tests for system APIs in {@link RestoreSession}
+ *
+ * <p>These tests use the local transport.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+@AppModeFull
+public class RestoreSessionHostSideTest extends BaseBackupHostSideTest {
+ private static final int USER_SYSTEM = 0;
+ private static final String MAIN_TEST_APP_PKG = "android.cts.backup.restoresessionapp";
+ private static final String DEVICE_MAIN_TEST_CLASS_NAME =
+ MAIN_TEST_APP_PKG + ".RestoreSessionTest";
+ private static final String MAIN_TEST_APK = "CtsRestoreSessionApp.apk";
+
+ private static final String TEST_APP_PKG_PREFIX = "android.cts.backup.restoresessionapp";
+ private static final String TEST_APP_APK_PREFIX = "CtsRestoreSessionApp";
+ private static final int TEST_APPS_COUNT = 3;
+
+ private Optional<String> mOldTransport = Optional.empty();
+ private BackupUtils mBackupUtils;
+
+ /** Switch to local transport. */
+ @Before
+ public void setUp() throws Exception {
+ mBackupUtils = getBackupUtils();
+ mOldTransport = Optional.of(setBackupTransport(mBackupUtils.getLocalTransportName()));
+ installPackage(MAIN_TEST_APK);
+ }
+
+ /** Restore transport settings to original values. */
+ @After
+ public void tearDown() throws Exception {
+ if (mOldTransport.isPresent()) {
+ setBackupTransport(mOldTransport.get());
+ mOldTransport = Optional.empty();
+
+ uninstallPackage(MAIN_TEST_APK);
+ }
+ }
+
+ /** Test {@link RestoreSession#restorePackages(long, RestoreObserver, Set)} */
+ @Test
+ public void testRestorePackages() throws Exception {
+ testRestorePackagesInternal("testRestorePackages");
+ }
+
+ /**
+ * Test {@link RestoreSession#restorePackages(long, RestoreObserver, Set, BackupManagerMonitor)}
+ */
+ @Test
+ public void testRestorePackagesWithMonitorParam() throws Exception {
+ testRestorePackagesInternal("testRestorePackagesWithMonitorParam");
+ }
+
+ /**
+ *
+ *
+ * <ol>
+ * <li>Install 3 test packages on the device
+ * <li>Write dummy values to shared preferences for each package
+ * <li>Backup each package (adb shell bmgr backupnow)
+ * <li>Clear shared preferences for each package
+ * <li>Run restore for 2 of the packages and verify that only they were restored
+ * <li>Verify that shared preferences for the 2 packages are restored correctly
+ * </ol>
+ */
+ private void testRestorePackagesInternal(String deviceTestName) throws Exception {
+ installPackage(getApkNameForTestApp(1));
+ installPackage(getApkNameForTestApp(2));
+ installPackage(getApkNameForTestApp(3));
+ //
+ // Write dummy value to shared preferences for all test packages.
+ checkRestoreSessionDeviceTestForAllApps("testSaveValuesToSharedPrefs");
+ checkRestoreSessionDeviceTestForAllApps("testCheckSharedPrefsExist");
+
+ // Backup all test packages.
+ mBackupUtils.backupNowAndAssertSuccess(getPackageNameForTestApp(1));
+ mBackupUtils.backupNowAndAssertSuccess(getPackageNameForTestApp(2));
+ mBackupUtils.backupNowAndAssertSuccess(getPackageNameForTestApp(3));
+
+ // Clear shared preferences for all test packages.
+ checkRestoreSessionDeviceTestForAllApps("testClearSharedPrefs");
+ checkRestoreSessionDeviceTestForAllApps("testCheckSharedPrefsDontExist");
+
+ runRestoreSessionDeviceTestAndAssertSuccess(
+ MAIN_TEST_APP_PKG, DEVICE_MAIN_TEST_CLASS_NAME, deviceTestName);
+
+ // Check that shared prefs are only restored (and restored correctly) for the first 2
+ // packages.
+ checkRestoreSessionDeviceTest(1, "testCheckSharedPrefsExist");
+ checkRestoreSessionDeviceTest(2, "testCheckSharedPrefsExist");
+ checkRestoreSessionDeviceTest(3, "testCheckSharedPrefsDontExist");
+
+ uninstallPackage(getPackageNameForTestApp(1));
+ uninstallPackage(getPackageNameForTestApp(2));
+ uninstallPackage(getPackageNameForTestApp(3));
+ }
+
+ /** Run the given device test for all test apps. */
+ private void checkRestoreSessionDeviceTestForAllApps(String testName)
+ throws DeviceNotAvailableException {
+ for (int appNumber = 1; appNumber <= TEST_APPS_COUNT; appNumber++) {
+ checkRestoreSessionDeviceTest(appNumber, testName);
+ }
+ }
+
+ /** Run device test with the given test name and test app number. */
+ private void checkRestoreSessionDeviceTest(int testAppNumber, String testName)
+ throws DeviceNotAvailableException {
+ String packageName = getPackageNameForTestApp(testAppNumber);
+ runRestoreSessionDeviceTestAndAssertSuccess(
+ packageName, packageName + ".RestoreSessionAppTest", testName);
+ }
+
+ private void runRestoreSessionDeviceTestAndAssertSuccess(
+ String packageName, String fullClassName, String testName)
+ throws DeviceNotAvailableException {
+ boolean result = runDeviceTests(packageName, fullClassName, testName);
+ assertTrue("Device test failed: " + testName, result);
+ }
+
+ private String getPackageNameForTestApp(int appNumber) {
+ return TEST_APP_PKG_PREFIX + appNumber;
+ }
+
+ private String getApkNameForTestApp(int appNumber) {
+ return TEST_APP_APK_PREFIX + appNumber + ".apk";
+ }
+
+ private String setBackupTransport(String transport) throws IOException {
+ return mBackupUtils.setBackupTransportForUser(transport, USER_SYSTEM);
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/Android.bp
similarity index 88%
rename from hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp
rename to hostsidetests/devicepolicy/app/HasLauncherActivityApp/Android.bp
index 2e8d9f4..f2c0649 100644
--- a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
android_test_helper_app {
- name: "CtsNoLaunchableActivityApp",
+ name: "CtsHasLauncherActivityApp",
// Don't include this package in any target
// When built, explicitly put it in the data partition.
dex_preopt: {
@@ -28,14 +28,13 @@
"cts",
"vts",
"general-tests",
- "cts_instant",
],
sdk_version: "current",
}
-// Build for no component app
+// Build for no launcher activity app
android_test_helper_app {
- name: "CtsNoComponentApp",
+ name: "CtsNoLauncherActivityApp",
dex_preopt: {
enabled: false,
},
@@ -48,9 +47,8 @@
"cts",
"vts",
"general-tests",
- "cts_instant",
],
- manifest: "no_component_AndroidManifest.xml",
+ manifest: "no_launcher_activity_AndroidManifest.xml",
sdk_version: "current",
}
@@ -69,7 +67,6 @@
"cts",
"vts",
"general-tests",
- "cts_instant",
],
manifest: "no_permission_AndroidManifest.xml",
sdk_version: "current",
diff --git a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/AndroidManifest.xml
similarity index 69%
copy from hostsidetests/devicepolicy/app/NoLaunchableActivityApp/AndroidManifest.xml
copy to hostsidetests/devicepolicy/app/HasLauncherActivityApp/AndroidManifest.xml
index 59f3767..760b31f 100755
--- a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/AndroidManifest.xml
@@ -16,9 +16,15 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.nolaunchableactivityapp">
+ package="com.android.cts.haslauncheractivityapp">
<uses-permission android:name="android.permission.INTERNET" />
- <application>
+ <application android:testOnly="true">
+ <activity android:name="com.android.cts.haslauncheractivityapp.MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
<service android:name=".EmptyService" android:enabled="true"></service>
</application>
diff --git a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/no_launcher_activity_AndroidManifest.xml
similarity index 71%
rename from hostsidetests/devicepolicy/app/NoLaunchableActivityApp/AndroidManifest.xml
rename to hostsidetests/devicepolicy/app/HasLauncherActivityApp/no_launcher_activity_AndroidManifest.xml
index 59f3767..ae2249a 100755
--- a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/no_launcher_activity_AndroidManifest.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -16,9 +16,14 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.nolaunchableactivityapp">
+ package="com.android.cts.nolauncheractivityapp">
<uses-permission android:name="android.permission.INTERNET" />
<application>
+ <activity android:name="com.android.cts.haslauncheractivityapp.MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ </intent-filter>
+ </activity>
<service android:name=".EmptyService" android:enabled="true"></service>
</application>
diff --git a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/no_permission_AndroidManifest.xml b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/no_permission_AndroidManifest.xml
similarity index 100%
rename from hostsidetests/devicepolicy/app/NoLaunchableActivityApp/no_permission_AndroidManifest.xml
rename to hostsidetests/devicepolicy/app/HasLauncherActivityApp/no_permission_AndroidManifest.xml
diff --git a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/src/com/android/cts/nolaunchableactivityapp/EmptyService.java b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/src/com/android/cts/haslauncheractivityapp/EmptyService.java
similarity index 94%
rename from hostsidetests/devicepolicy/app/NoLaunchableActivityApp/src/com/android/cts/nolaunchableactivityapp/EmptyService.java
rename to hostsidetests/devicepolicy/app/HasLauncherActivityApp/src/com/android/cts/haslauncheractivityapp/EmptyService.java
index 6cd0da6..80f9ee5 100644
--- a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/src/com/android/cts/nolaunchableactivityapp/EmptyService.java
+++ b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/src/com/android/cts/haslauncheractivityapp/EmptyService.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.nolaunchableactivityapp;
+package com.android.cts.haslaunchableactivityapp;
import android.app.Service;
import android.content.Intent;
diff --git a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/no_component_AndroidManifest.xml b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/src/com/android/cts/haslauncheractivityapp/MainActivity.java
old mode 100755
new mode 100644
similarity index 63%
rename from hostsidetests/devicepolicy/app/NoLaunchableActivityApp/no_component_AndroidManifest.xml
rename to hostsidetests/devicepolicy/app/HasLauncherActivityApp/src/com/android/cts/haslauncheractivityapp/MainActivity.java
index a48cb1d..1f2b2cb
--- a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/no_component_AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/src/com/android/cts/haslauncheractivityapp/MainActivity.java
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2019 The Android Open Source Project
+/*
+ * 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.
@@ -13,13 +12,11 @@
* 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.cts.haslaunchableactivityapp;
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.nocomponentapp">
+import android.app.Activity;
- <uses-permission android:name="android.permission.INTERNET" />
- <application />
-
-</manifest>
+public class MainActivity extends Activity {
+}
diff --git a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
index 45032a4..cade532 100644
--- a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
+++ b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
@@ -42,7 +42,9 @@
import android.test.AndroidTestCase;
import androidx.test.InstrumentationRegistry;
+import com.android.compatibility.common.util.SystemUtil;
+import java.io.IOException;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@@ -53,12 +55,14 @@
public class LauncherAppsTests extends AndroidTestCase {
public static final String SIMPLE_APP_PACKAGE = "com.android.cts.launcherapps.simpleapp";
- private static final String NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE =
- "com.android.cts.nolaunchableactivityapp";
- private static final String NO_COMPONENT_APP_PACKAGE =
- "com.android.cts.nocomponentapp";
+ private static final String HAS_LAUNCHER_ACTIVITY_APP_PACKAGE =
+ "com.android.cts.haslauncheractivityapp";
+ private static final String NO_LAUNCHER_ACTIVITY_APP_PACKAGE =
+ "com.android.cts.nolauncheractivityapp";
private static final String NO_PERMISSION_APP_PACKAGE =
"com.android.cts.nopermissionapp";
+ private static final String LAUNCHER_ACTIVITY_COMPONENT =
+ "com.android.cts.haslauncheractivityapp/.MainActivity";
private static final String SYNTHETIC_APP_DETAILS_ACTIVITY = "android.app.AppDetailsActivity";
@@ -216,15 +220,17 @@
assertFalse(mLauncherApps.isPackageEnabled("android", mUser));
}
- public void testNoLaunchableActivityAppHasAppDetailsActivityInjected() throws Exception {
- // NoLaunchableActivityApp is installed for duration of this test - make sure
+ public void testHasLauncherActivityAppHasAppDetailsActivityInjected() throws Exception {
+ // HasLauncherActivityApp is installed for duration of this test - make sure
// it's present on the activity list, has the synthetic activity generated, and it's
// enabled and exported
- assertActivityInjected(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE);
+ disableLauncherActivity();
+ assertActivityInjected(HAS_LAUNCHER_ACTIVITY_APP_PACKAGE);
}
public void testGetSetSyntheticAppDetailsActivityEnabled() throws Exception {
- assertActivityInjected(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE);
+ disableLauncherActivity();
+ assertActivityInjected(HAS_LAUNCHER_ACTIVITY_APP_PACKAGE);
PackageManager pm = mInstrumentation.getContext().getPackageManager();
try {
pm.setSyntheticAppDetailsActivityEnabled(mContext.getPackageName(), false);
@@ -233,7 +239,7 @@
// Expected: No permission
}
try {
- pm.setSyntheticAppDetailsActivityEnabled(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE, false);
+ pm.setSyntheticAppDetailsActivityEnabled(HAS_LAUNCHER_ACTIVITY_APP_PACKAGE, false);
fail("Should not able to change other app's app details activity state");
} catch (SecurityException e) {
// Expected: No permission
@@ -241,17 +247,17 @@
mInstrumentation.getUiAutomation().adoptShellPermissionIdentity();
try {
assertTrue(
- pm.getSyntheticAppDetailsActivityEnabled(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE));
+ pm.getSyntheticAppDetailsActivityEnabled(HAS_LAUNCHER_ACTIVITY_APP_PACKAGE));
// Disable app details activity and assert if the change is applied
- pm.setSyntheticAppDetailsActivityEnabled(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE, false);
+ pm.setSyntheticAppDetailsActivityEnabled(HAS_LAUNCHER_ACTIVITY_APP_PACKAGE, false);
assertFalse(
- pm.getSyntheticAppDetailsActivityEnabled(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE));
- assertInjectedActivityNotFound(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE);
+ pm.getSyntheticAppDetailsActivityEnabled(HAS_LAUNCHER_ACTIVITY_APP_PACKAGE));
+ assertInjectedActivityNotFound(HAS_LAUNCHER_ACTIVITY_APP_PACKAGE);
// Enable app details activity and assert if the change is applied
- pm.setSyntheticAppDetailsActivityEnabled(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE, true);
+ pm.setSyntheticAppDetailsActivityEnabled(HAS_LAUNCHER_ACTIVITY_APP_PACKAGE, true);
assertTrue(
- pm.getSyntheticAppDetailsActivityEnabled(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE));
- assertActivityInjected(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE);
+ pm.getSyntheticAppDetailsActivityEnabled(HAS_LAUNCHER_ACTIVITY_APP_PACKAGE));
+ assertActivityInjected(HAS_LAUNCHER_ACTIVITY_APP_PACKAGE);
} finally {
mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
}
@@ -262,10 +268,10 @@
assertActivityInjected(MANAGED_PROFILE_PKG);
}
- public void testNoComponentAppNotInjected() throws Exception {
- // NoComponentApp is installed for duration of this test - make sure
+ public void testNoLauncherActivityAppNotInjected() throws Exception {
+ // NoLauncherActivityApp is installed for duration of this test - make sure
// it's NOT present on the activity list
- assertInjectedActivityNotFound(NO_COMPONENT_APP_PACKAGE);
+ assertInjectedActivityNotFound(NO_LAUNCHER_ACTIVITY_APP_PACKAGE);
}
public void testNoPermissionAppNotInjected() throws Exception {
@@ -275,10 +281,11 @@
}
public void testDoPoNoTestAppInjectedActivityFound() throws Exception {
- // NoLaunchableActivityApp is installed for duration of this test - make sure
+ // HasLauncherActivityApp is installed for duration of this test - make sure
// it's NOT present on the activity list For example, DO / PO mode won't show icons.
// This test is being called by DeviceOwnerTest.
- assertInjectedActivityNotFound(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE);
+ disableLauncherActivity();
+ assertInjectedActivityNotFound(HAS_LAUNCHER_ACTIVITY_APP_PACKAGE);
}
public void testProfileOwnerInjectedActivityNotFound() throws Exception {
@@ -305,6 +312,11 @@
}
}
+ private void disableLauncherActivity() throws IOException {
+ SystemUtil.runShellCommand(mInstrumentation,
+ "pm disable --user " + mUser.getIdentifier() + " " + LAUNCHER_ACTIVITY_COMPONENT);
+ }
+
private void expectSecurityException(ExceptionRunnable action, String failMessage)
throws Exception {
try {
@@ -336,7 +348,7 @@
if (compName.getPackageName().equals(targetPackage)) {
noLaunchableActivityAppFound = true;
// make sure it points to the synthetic app details activity
- assertEquals(activity.getName(), SYNTHETIC_APP_DETAILS_ACTIVITY);
+ assertEquals(SYNTHETIC_APP_DETAILS_ACTIVITY, activity.getName());
// make sure it's both exported and enabled
try {
PackageManager pm = mInstrumentation.getContext().getPackageManager();
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index d223f03..7c335c2 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -72,8 +72,8 @@
private static final String ARG_NETWORK_LOGGING_BATCH_COUNT = "batchCount";
private static final String TEST_UPDATE_LOCATION = "/data/local/tmp/cts/deviceowner";
- private static final String LAUNCHER_TESTS_NO_LAUNCHABLE_ACTIVITY_APK =
- "CtsNoLaunchableActivityApp.apk";
+ private static final String LAUNCHER_TESTS_HAS_LAUNCHER_ACTIVITY_APK =
+ "CtsHasLauncherActivityApp.apk";
/**
* Copied from {@link android.app.admin.DevicePolicyManager
@@ -995,7 +995,7 @@
// Install app to primary user
installAppAsUser(BaseLauncherAppsTest.LAUNCHER_TESTS_APK, mPrimaryUserId);
installAppAsUser(BaseLauncherAppsTest.LAUNCHER_TESTS_SUPPORT_APK, mPrimaryUserId);
- installAppAsUser(LAUNCHER_TESTS_NO_LAUNCHABLE_ACTIVITY_APK, mPrimaryUserId);
+ installAppAsUser(LAUNCHER_TESTS_HAS_LAUNCHER_ACTIVITY_APK, mPrimaryUserId);
// Run test to check if launcher api shows hidden app
String mSerialNumber = Integer.toString(getUserSerialNumber(USER_SYSTEM));
@@ -1005,7 +1005,7 @@
mPrimaryUserId, Collections.singletonMap(BaseLauncherAppsTest.PARAM_TEST_USER,
mSerialNumber));
} finally {
- getDevice().uninstallPackage(LAUNCHER_TESTS_NO_LAUNCHABLE_ACTIVITY_APK);
+ getDevice().uninstallPackage(LAUNCHER_TESTS_HAS_LAUNCHER_ACTIVITY_APK);
getDevice().uninstallPackage(BaseLauncherAppsTest.LAUNCHER_TESTS_SUPPORT_APK);
getDevice().uninstallPackage(BaseLauncherAppsTest.LAUNCHER_TESTS_APK);
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
index c756d16..f8a78f0 100755
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
@@ -31,8 +31,8 @@
private static final String MANAGED_PROFILE_APK = "CtsManagedProfileApp.apk";
private static final String ADMIN_RECEIVER_TEST_CLASS =
MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
- private static final String LAUNCHER_TESTS_NO_LAUNCHABLE_ACTIVITY_APK =
- "CtsNoLaunchableActivityApp.apk";
+ private static final String LAUNCHER_TESTS_HAS_LAUNCHER_ACTIVITY_APK =
+ "CtsHasLauncherActivityApp.apk";
private int mProfileUserId;
private int mParentUserId;
@@ -65,7 +65,7 @@
if (mHasFeature) {
removeUser(mProfileUserId);
uninstallTestApps();
- getDevice().uninstallPackage(LAUNCHER_TESTS_NO_LAUNCHABLE_ACTIVITY_APK);
+ getDevice().uninstallPackage(LAUNCHER_TESTS_HAS_LAUNCHER_ACTIVITY_APK);
}
super.tearDown();
}
@@ -122,15 +122,15 @@
return;
}
// Install app for all users.
- installAppAsUser(LAUNCHER_TESTS_NO_LAUNCHABLE_ACTIVITY_APK, mParentUserId);
- installAppAsUser(LAUNCHER_TESTS_NO_LAUNCHABLE_ACTIVITY_APK, mProfileUserId);
+ installAppAsUser(LAUNCHER_TESTS_HAS_LAUNCHER_ACTIVITY_APK, mParentUserId);
+ installAppAsUser(LAUNCHER_TESTS_HAS_LAUNCHER_ACTIVITY_APK, mProfileUserId);
// Run tests to check SimpleApp exists in both profile and main user.
runDeviceTestsAsUser(LAUNCHER_TESTS_PKG,
LAUNCHER_TESTS_CLASS, "testDoPoNoTestAppInjectedActivityFound",
mParentUserId, Collections.singletonMap(PARAM_TEST_USER, mProfileSerialNumber));
runDeviceTestsAsUser(LAUNCHER_TESTS_PKG,
- LAUNCHER_TESTS_CLASS, "testNoLaunchableActivityAppHasAppDetailsActivityInjected",
+ LAUNCHER_TESTS_CLASS, "testHasLauncherActivityAppHasAppDetailsActivityInjected",
mParentUserId, Collections.singletonMap(PARAM_TEST_USER, mMainUserSerialNumber));
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LimitAppIconHidingTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LimitAppIconHidingTest.java
index 09d2541..fa92065 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LimitAppIconHidingTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LimitAppIconHidingTest.java
@@ -23,10 +23,10 @@
*/
public class LimitAppIconHidingTest extends BaseLauncherAppsTest {
- private static final String LAUNCHER_TESTS_NO_LAUNCHABLE_ACTIVITY_APK =
- "CtsNoLaunchableActivityApp.apk";
- private static final String LAUNCHER_TESTS_NO_COMPONENT_APK =
- "CtsNoComponentApp.apk";
+ private static final String LAUNCHER_TESTS_HAS_LAUNCHER_ACTIVITY_APK =
+ "CtsHasLauncherActivityApp.apk";
+ private static final String LAUNCHER_TESTS_NO_LAUNCHER_ACTIVITY_APK =
+ "CtsNoLauncherActivityApp.apk";
private static final String LAUNCHER_TESTS_NO_PERMISSION_APK =
"CtsNoPermissionApp.apk";
@@ -58,8 +58,8 @@
@Override
protected void installTestApps(int userId) throws Exception {
super.installTestApps(mCurrentUserId);
- installAppAsUser(LAUNCHER_TESTS_NO_LAUNCHABLE_ACTIVITY_APK, mCurrentUserId);
- installAppAsUser(LAUNCHER_TESTS_NO_COMPONENT_APK, mCurrentUserId);
+ installAppAsUser(LAUNCHER_TESTS_HAS_LAUNCHER_ACTIVITY_APK, mCurrentUserId);
+ installAppAsUser(LAUNCHER_TESTS_NO_LAUNCHER_ACTIVITY_APK, mCurrentUserId);
installAppAsUser(LAUNCHER_TESTS_NO_PERMISSION_APK, mCurrentUserId);
}
@@ -67,16 +67,16 @@
protected void uninstallTestApps() throws Exception {
super.uninstallTestApps();
getDevice().uninstallPackage(LAUNCHER_TESTS_NO_PERMISSION_APK);
- getDevice().uninstallPackage(LAUNCHER_TESTS_NO_COMPONENT_APK);
- getDevice().uninstallPackage(LAUNCHER_TESTS_NO_LAUNCHABLE_ACTIVITY_APK);
+ getDevice().uninstallPackage(LAUNCHER_TESTS_NO_LAUNCHER_ACTIVITY_APK);
+ getDevice().uninstallPackage(LAUNCHER_TESTS_HAS_LAUNCHER_ACTIVITY_APK);
}
- public void testNoLaunchableActivityAppHasAppDetailsActivityInjected() throws Exception {
+ public void testHasLauncherActivityAppHasAppDetailsActivityInjected() throws Exception {
if (!mHasLauncherApps) {
return;
}
runDeviceTestsAsUser(LAUNCHER_TESTS_PKG,
- LAUNCHER_TESTS_CLASS, "testNoLaunchableActivityAppHasAppDetailsActivityInjected",
+ LAUNCHER_TESTS_CLASS, "testHasLauncherActivityAppHasAppDetailsActivityInjected",
mCurrentUserId, Collections.singletonMap(PARAM_TEST_USER, mSerialNumber));
}
@@ -89,12 +89,12 @@
mCurrentUserId, Collections.singletonMap(PARAM_TEST_USER, mSerialNumber));
}
- public void testNoComponentAppNotInjected() throws Exception {
+ public void testNoLauncherActivityAppNotInjected() throws Exception {
if (!mHasLauncherApps) {
return;
}
runDeviceTestsAsUser(LAUNCHER_TESTS_PKG,
- LAUNCHER_TESTS_CLASS, "testNoComponentAppNotInjected",
+ LAUNCHER_TESTS_CLASS, "testNoLauncherActivityAppNotInjected",
mCurrentUserId, Collections.singletonMap(PARAM_TEST_USER, mSerialNumber));
}
diff --git a/hostsidetests/os/src/android/os/cts/OsHostTests.java b/hostsidetests/os/src/android/os/cts/OsHostTests.java
index 8e3f5c5..44e8a79 100644
--- a/hostsidetests/os/src/android/os/cts/OsHostTests.java
+++ b/hostsidetests/os/src/android/os/cts/OsHostTests.java
@@ -17,6 +17,7 @@
package android.os.cts;
import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.SecurityTest;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.tradefed.build.IBuildInfo;
@@ -118,6 +119,7 @@
* Test behavior of malformed Notifications w.r.t. foreground services
* @throws Exception
*/
+ @SecurityTest(minPatchLevel = "2020-05")
@AppModeFull(reason = "Instant apps may not start foreground services")
public void testForegroundServiceBadNotification() throws Exception {
final Pattern pattern = Pattern.compile(FILTER_FG_SERVICE_REGEXP);
diff --git a/hostsidetests/securitybulletin/AndroidTest.xml b/hostsidetests/securitybulletin/AndroidTest.xml
index 566296a..86eb379 100644
--- a/hostsidetests/securitybulletin/AndroidTest.xml
+++ b/hostsidetests/securitybulletin/AndroidTest.xml
@@ -230,6 +230,7 @@
<!-- Bulletin 2020-03 -->
<!-- Please add tests solely from this bulletin below to avoid merge conflict -->
<option name="push" value="CVE-2020-0069->/data/local/tmp/CVE-2020-0069" />
+
<option name="append-bitness" value="true" />
</target_preparer>
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index c6e0b74..045b4a1 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -547,15 +547,6 @@
if (!hasFeature(FEATURE_WATCH, false)) return;
final int atomTag = Atom.MEDIA_CODEC_STATE_CHANGED_FIELD_NUMBER;
- // 5 seconds. Starting video tends to be much slower than most other
- // tests on slow devices. This is unfortunate, because it leaves a
- // really big slop in assertStatesOccurred. It would be better if
- // assertStatesOccurred had a tighter range on large timeouts.
- final int waitTime = 5000;
-
- // From {@link VideoPlayerActivity#DELAY_MILLIS}
- final int videoDuration = 2000;
-
Set<Integer> onState = new HashSet<>(
Arrays.asList(MediaCodecStateChanged.State.ON_VALUE));
Set<Integer> offState = new HashSet<>(
@@ -573,7 +564,7 @@
List<EventMetricData> data = getEventMetricDataList();
// Assert that the events happened in the expected order.
- assertStatesOccurred(stateSet, data, videoDuration,
+ assertStatesOccurred(stateSet, data, WAIT_TIME_LONG,
atom -> atom.getMediaCodecStateChanged().getState().getNumber());
}
@@ -1075,8 +1066,9 @@
try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
"action.show_notification")) {
Thread.sleep(WAIT_TIME_SHORT);
- // Trigger new pull.
+ // Trigger a pull and wait for new pull before killing the process.
setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_LONG);
}
// Assert about ProcessMemoryState for the test app.
@@ -1116,6 +1108,7 @@
// Trigger new pull.
setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_LONG);
// Assert about NativeProcessMemoryState for statsd.
List<Atom> atoms = getGaugeMetricDataList();
@@ -1152,8 +1145,8 @@
try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
"action.show_notification")) {
setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_LONG);
}
- Thread.sleep(WAIT_TIME_SHORT);
// Assert about ProcessMemoryHighWaterMark for the test app, statsd and system server.
List<Atom> atoms = getGaugeMetricDataList();
diff --git a/tests/app/DownloadManagerLegacyTest/src/android/app/cts/DownloadManagerLegacyTest.java b/tests/app/DownloadManagerLegacyTest/src/android/app/cts/DownloadManagerLegacyTest.java
index 48ae060..ebe54b3 100644
--- a/tests/app/DownloadManagerLegacyTest/src/android/app/cts/DownloadManagerLegacyTest.java
+++ b/tests/app/DownloadManagerLegacyTest/src/android/app/cts/DownloadManagerLegacyTest.java
@@ -15,6 +15,7 @@
*/
package android.app.cts;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -23,6 +24,8 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
+import android.os.FileUtils;
+import android.provider.MediaStore;
import androidx.test.runner.AndroidJUnit4;
@@ -30,6 +33,9 @@
import org.junit.runner.RunWith;
import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
@RunWith(AndroidJUnit4.class)
public class DownloadManagerLegacyTest extends DownloadManagerTestBase {
@@ -84,25 +90,33 @@
*/
@Test
public void testAddCompletedDownload_mediaStoreEntry() throws Exception {
+ final String assetName = "testmp3.mp3";
final String[] downloadPaths = {
new File(Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_DOWNLOADS), "file1.txt").getPath(),
- "/sdcard/Download/file2.txt",
+ Environment.DIRECTORY_DOWNLOADS), "file1.mp3").getPath(),
+ "/sdcard/Download/file2.mp3",
};
for (String downloadLocation : downloadPaths) {
- final String fileContents =
- "Test content:" + downloadLocation + "_" + System.nanoTime();
final File file = new File(downloadLocation);
- writeToFile(file, fileContents);
+ try (InputStream in = mContext.getAssets().open(assetName);
+ OutputStream out = new FileOutputStream(file)) {
+ FileUtils.copy(in, out);
+ }
final long downloadId = mDownloadManager.addCompletedDownload(
file.getName(), "Test desc",
- true, "text/plain", downloadLocation, fileContents.getBytes().length, true);
+ true, "audio/mp3", downloadLocation, 0, true);
assertTrue(downloadId >= 0);
final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(downloadId);
final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
- assertEquals(fileContents, readContentsFromUri(mediaStoreUri));
+ assertArrayEquals(hash(mContext.getAssets().open(assetName)),
+ hash(mContext.getContentResolver().openInputStream(mediaStoreUri)));
+
+ assertEquals("1", getMediaStoreColumnValue(mediaStoreUri,
+ MediaStore.Audio.AudioColumns.IS_MUSIC));
+ assertEquals(new File(downloadLocation).length(), Long.parseLong(
+ getMediaStoreColumnValue(mediaStoreUri, MediaStore.Audio.AudioColumns.SIZE)));
// Delete entry in DownloadProvider and verify it's deleted from MediaProvider as well.
assertRemoveDownload(downloadId, 0);
diff --git a/tests/app/app/assets/testmp3.mp3 b/tests/app/app/assets/testmp3.mp3
new file mode 100644
index 0000000..657faf7
--- /dev/null
+++ b/tests/app/app/assets/testmp3.mp3
Binary files differ
diff --git a/tests/app/app/assets/testvideo.3gp b/tests/app/app/assets/testvideo.3gp
new file mode 100644
index 0000000..1503272
--- /dev/null
+++ b/tests/app/app/assets/testvideo.3gp
Binary files differ
diff --git a/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/app/src/android/app/cts/DownloadManagerTest.java
index e1a4591..4d1ecfa 100644
--- a/tests/app/src/android/app/cts/DownloadManagerTest.java
+++ b/tests/app/src/android/app/cts/DownloadManagerTest.java
@@ -36,7 +36,9 @@
import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
+import android.provider.MediaStore;
import android.util.Log;
+import android.util.Pair;
import androidx.test.filters.FlakyTest;
import androidx.test.InstrumentationRegistry;
@@ -49,6 +51,7 @@
import java.io.File;
import java.io.FileInputStream;
+import java.io.InputStream;
@RunWith(AndroidJUnit4.class)
public class DownloadManagerTest extends DownloadManagerTestBase {
@@ -585,4 +588,59 @@
// expected
}
}
+
+ @Test
+ public void testDownload_mediaScanned() throws Exception {
+ final String[] destinations = {
+ Environment.DIRECTORY_MUSIC,
+ Environment.DIRECTORY_DOWNLOADS,
+ };
+ final String[] subPaths = {
+ "testmp3.mp3",
+ "testvideo.3gp",
+ };
+ final Pair<String, String>[] expectedMediaAttributes = new Pair[] {
+ Pair.create(MediaStore.Audio.AudioColumns.IS_MUSIC, "1"),
+ Pair.create(MediaStore.Video.VideoColumns.DURATION, "11047"),
+ };
+
+ for (int i = 0; i < destinations.length; ++i) {
+ final String destination = destinations[i];
+ final String subPath = subPaths[i];
+
+ final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+ try {
+ IntentFilter intentFilter = new IntentFilter(
+ DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+ mContext.registerReceiver(receiver, intentFilter);
+
+ DownloadManager.Request requestPublic = new DownloadManager.Request(
+ getAssetUrl(subPath));
+ requestPublic.setDestinationInExternalPublicDir(destination, subPath);
+ long id = mDownloadManager.enqueue(requestPublic);
+
+ int allDownloads = getTotalNumberDownloads();
+ assertEquals(1, allDownloads);
+
+ receiver.waitForDownloadComplete(SHORT_TIMEOUT, id);
+ assertSuccessfulDownload(id, new File(
+ Environment.getExternalStoragePublicDirectory(destination), subPath));
+
+ final Uri downloadUri = mDownloadManager.getUriForDownloadedFile(id);
+ mContext.grantUriPermission("com.android.shell", downloadUri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ final Uri mediaStoreUri = getMediaStoreUri(downloadUri);
+ assertEquals(expectedMediaAttributes[i].second,
+ getMediaStoreColumnValue(mediaStoreUri, expectedMediaAttributes[i].first));
+ final int expectedSize = getTotalBytes(
+ mContext.getContentResolver().openInputStream(downloadUri));
+ assertEquals(expectedSize, Integer.parseInt(getMediaStoreColumnValue(
+ mediaStoreUri, MediaStore.MediaColumns.SIZE)));
+
+ assertRemoveDownload(id, 0);
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+ }
}
diff --git a/tests/app/src/android/app/cts/DownloadManagerTestBase.java b/tests/app/src/android/app/cts/DownloadManagerTestBase.java
index ddb3c17..eaab0e3 100644
--- a/tests/app/src/android/app/cts/DownloadManagerTestBase.java
+++ b/tests/app/src/android/app/cts/DownloadManagerTestBase.java
@@ -22,6 +22,7 @@
import android.app.DownloadManager;
import android.content.BroadcastReceiver;
+import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -31,6 +32,7 @@
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.SystemClock;
+import android.provider.MediaStore;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
@@ -121,6 +123,65 @@
}
}
+ protected String getMediaStoreColumnValue(Uri mediaStoreUri, String columnName)
+ throws Exception {
+ if (!MediaStore.Files.FileColumns.MEDIA_TYPE.equals(columnName)) {
+ final int mediaType = getMediaType(mediaStoreUri);
+ final String volumeName = MediaStore.getVolumeName(mediaStoreUri);
+ final long id = ContentUris.parseId(mediaStoreUri);
+ switch (mediaType) {
+ case MediaStore.Files.FileColumns.MEDIA_TYPE_AUDIO:
+ mediaStoreUri = ContentUris.withAppendedId(
+ MediaStore.Audio.Media.getContentUri(volumeName), id);
+ break;
+ case MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE:
+ mediaStoreUri = ContentUris.withAppendedId(
+ MediaStore.Images.Media.getContentUri(volumeName), id);
+ break;
+ case MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO:
+ mediaStoreUri = ContentUris.withAppendedId(
+ MediaStore.Video.Media.getContentUri(volumeName), id);
+ break;
+ }
+ }
+ // Need to pass in the user id to support multi-user scenarios.
+ final int userId = getUserId();
+ final String cmd = String.format("content query --uri %s --projection %s --user %s",
+ mediaStoreUri, columnName, userId);
+ final String res = runShellCommand(cmd).trim();
+ final String str = columnName + "=";
+ final int i = res.indexOf(str);
+ if (i >= 0) {
+ return res.substring(i + str.length());
+ } else {
+ throw new FileNotFoundException("Failed to find "
+ + columnName + " for "
+ + mediaStoreUri + "; found " + res);
+ }
+ }
+
+ private int getMediaType(Uri mediaStoreUri) throws Exception {
+ final Uri filesUri = MediaStore.Files.getContentUri(
+ MediaStore.getVolumeName(mediaStoreUri),
+ ContentUris.parseId(mediaStoreUri));
+ return Integer.parseInt(getMediaStoreColumnValue(filesUri,
+ MediaStore.Files.FileColumns.MEDIA_TYPE));
+ }
+
+ protected int getTotalBytes(InputStream in) throws Exception {
+ try {
+ int total = 0;
+ final byte[] buf = new byte[4096];
+ int bytesRead;
+ while ((bytesRead = in.read(buf)) != -1) {
+ total += bytesRead;
+ }
+ return total;
+ } finally {
+ FileUtils.closeQuietly(in);
+ }
+ }
+
private static int getUserId() {
return Process.myUserHandle().getIdentifier();
}
diff --git a/tests/apppredictionservice/src/android/apppredictionservice/cts/AppPredictionServiceTest.java b/tests/apppredictionservice/src/android/apppredictionservice/cts/AppPredictionServiceTest.java
index dbc5f38..8e92d9a 100644
--- a/tests/apppredictionservice/src/android/apppredictionservice/cts/AppPredictionServiceTest.java
+++ b/tests/apppredictionservice/src/android/apppredictionservice/cts/AppPredictionServiceTest.java
@@ -39,6 +39,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.RequiredServiceRule;
import com.android.compatibility.common.util.SystemUtil;
import org.junit.After;
@@ -47,6 +48,7 @@
import org.junit.runner.RunWith;
import java.util.ArrayList;
+import org.junit.ClassRule;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
@@ -66,6 +68,10 @@
private static final String TEST_LAUNCH_LOCATION = "testCollapsedLocation";
private static final int TEST_ACTION = 2;
+ @ClassRule
+ public static final RequiredServiceRule mRequiredServiceRule =
+ new RequiredServiceRule(APP_PREDICTION_SERVICE);
+
private ServiceReporter mReporter;
private Bundle mPredictionContextExtras;
diff --git a/tests/contentcaptureservice/AndroidManifest.xml b/tests/contentcaptureservice/AndroidManifest.xml
index 7f2c108..f0d262f 100644
--- a/tests/contentcaptureservice/AndroidManifest.xml
+++ b/tests/contentcaptureservice/AndroidManifest.xml
@@ -22,6 +22,49 @@
<uses-library android:name="android.test.runner" />
+ <activity android:name=".BlankActivity"
+ android:label="Blank"
+ android:taskAffinity=".BlankActivity"
+ android:theme="@android:style/Theme.NoTitleBar">
+ <intent-filter>
+ <!-- This intent filter is not really needed by CTS, but it makes easier to launch
+ this app during CTS development... -->
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".BlankWithTitleActivity"
+ android:label="Blanka"
+ android:taskAffinity=".BlankWithTitleActivity">
+ <intent-filter>
+ <!-- This intent filter is not really needed by CTS, but it makes easier to launch
+ this app during CTS development... -->
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".LoginActivity"
+ android:label="Login"
+ android:taskAffinity=".LoginActivity"
+ android:theme="@android:style/Theme.NoTitleBar">
+ <intent-filter>
+ <!-- This intent filter is not really needed by CTS, but it makes easier to launch
+ this app during CTS development... -->
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".ChildlessActivity"
+ android:label="Childless"
+ android:taskAffinity=".ChildlessActivity"
+ android:theme="@android:style/Theme.NoTitleBar">
+ <intent-filter>
+ <!-- This intent filter is not really needed by CTS, but it makes easier to launch
+ this app during CTS development... -->
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
<activity android:name=".CustomViewActivity"
android:label="CustomView"
android:taskAffinity=".CustomViewActivity"
diff --git a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/AndroidManifest.xml b/tests/contentcaptureservice/res/layout/childless_activity.xml
old mode 100755
new mode 100644
similarity index 63%
copy from hostsidetests/devicepolicy/app/NoLaunchableActivityApp/AndroidManifest.xml
copy to tests/contentcaptureservice/res/layout/childless_activity.xml
index 59f3767..8cc67d5
--- a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/AndroidManifest.xml
+++ b/tests/contentcaptureservice/res/layout/childless_activity.xml
@@ -13,14 +13,14 @@
* 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.cts.nolaunchableactivityapp">
- <uses-permission android:name="android.permission.INTERNET" />
- <application>
- <service android:name=".EmptyService" android:enabled="true"></service>
- </application>
-
-</manifest>
-
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/root_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical" >
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/contentcaptureservice/res/layout/login_activity.xml b/tests/contentcaptureservice/res/layout/login_activity.xml
new file mode 100644
index 0000000..1164b4f
--- /dev/null
+++ b/tests/contentcaptureservice/res/layout/login_activity.xml
@@ -0,0 +1,50 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/root_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/username_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Username" />
+
+ <EditText
+ android:id="@+id/username"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/password_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Password" />
+
+ <EditText
+ android:id="@+id/password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
index e7f2e68..8f77b2b 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractContentCaptureIntegrationTest.java
@@ -114,7 +114,7 @@
mServiceWatcher.waitOnDestroy();
}
} catch (Throwable t) {
- Log.e(TAG, "error disablign service", t);
+ Log.e(TAG, "error disabling service", t);
}
}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractRootViewActivity.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractRootViewActivity.java
new file mode 100644
index 0000000..bd4fecc
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AbstractRootViewActivity.java
@@ -0,0 +1,121 @@
+/*
+ * 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 android.contentcaptureservice.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import androidx.annotation.NonNull;
+
+import com.android.compatibility.common.util.DoubleVisitor;
+
+/**
+ * Base class for classes that have a {@code root_view} root view.
+ */
+abstract class AbstractRootViewActivity extends AbstractContentCaptureActivity {
+
+ private static final String TAG = AbstractRootViewActivity.class.getSimpleName();
+
+ private static DoubleVisitor<AbstractRootViewActivity, LinearLayout> sRootViewVisitor;
+ private static DoubleVisitor<AbstractRootViewActivity, LinearLayout> sOnAnimationVisitor;
+
+ private LinearLayout mRootView;
+
+ /**
+ * Sets a visitor called when the activity is created.
+ */
+ static void onRootView(@NonNull DoubleVisitor<AbstractRootViewActivity, LinearLayout> visitor) {
+ sRootViewVisitor = visitor;
+ }
+
+ /**
+ * Sets a visitor to be called on {@link Activity#onEnterAnimationComplete()}.
+ */
+ static void onAnimationComplete(
+ @NonNull DoubleVisitor<AbstractRootViewActivity, LinearLayout> visitor) {
+ sOnAnimationVisitor = visitor;
+ }
+
+ @Override
+ protected final void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentViewOnCreate(savedInstanceState);
+
+ mRootView = findViewById(R.id.root_view);
+
+ Log.d(TAG, "onCreate(): parents for " + getClass() + ": rootView=" + mRootView
+ + "\ngrandParent=" + getGrandParent()
+ + "\ngrandGrandParent=" + getGrandGrandParent());
+
+ if (sRootViewVisitor != null) {
+ Log.d(TAG, "Applying visitor to " + this + "/" + mRootView);
+ try {
+ sRootViewVisitor.visit(this, mRootView);
+ } finally {
+ sRootViewVisitor = null;
+ }
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ Log.d(TAG, "AutofillIds for " + getClass() + ": "
+ + " rootView=" + getRootView().getAutofillId()
+ + ", grandParent=" + getGrandParent().getAutofillId()
+ + ", grandGrandParent=" + getGrandGrandParent().getAutofillId());
+ }
+
+ @Override
+ public void onEnterAnimationComplete() {
+ if (sOnAnimationVisitor != null) {
+ Log.i(TAG, "onEnterAnimationComplete(): applying visitor on " + this);
+ try {
+ sOnAnimationVisitor.visit(this, mRootView);
+ } finally {
+ sOnAnimationVisitor = null;
+ }
+ } else {
+ Log.i(TAG, "onEnterAnimationComplete(): no visitor on " + this);
+ }
+ }
+
+ public LinearLayout getRootView() {
+ return mRootView;
+ }
+
+ // TODO(b/122315042): remove this method when not needed anymore
+ @NonNull
+ public ViewGroup getGrandParent() {
+ return (ViewGroup) mRootView.getParent();
+ }
+
+ // TODO(b/122315042): remove this method when not needed anymore
+ @NonNull
+ public ViewGroup getGrandGrandParent() {
+ return (ViewGroup) getGrandParent().getParent();
+ }
+
+ /**
+ * The real "onCreate" method that should be extended by subclasses.
+ *
+ */
+ protected abstract void setContentViewOnCreate(Bundle savedInstanceState);
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java
index 5203460..0f7e298 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Assertions.java
@@ -226,6 +226,22 @@
assertSessionId(expectedSessionId, expectedView);
}
+ /**
+ * Asserts the contents of a {@link #TYPE_VIEW_TREE_APPEARING} event.
+ */
+ public static void assertViewTreeStarted(@NonNull List<ContentCaptureEvent> events,
+ int index) {
+ assertSessionLevelEvent(events, index, TYPE_VIEW_TREE_APPEARING);
+ }
+
+ /**
+ * Asserts the contents of a {@link #TYPE_VIEW_TREE_APPEARED} event.
+ */
+ public static void assertViewTreeFinished(@NonNull List<ContentCaptureEvent> events,
+ int index) {
+ assertSessionLevelEvent(events, index, TYPE_VIEW_TREE_APPEARED);
+ }
+
private static void assertSessionLevelEvent(@NonNull List<ContentCaptureEvent> events,
int index, int expectedType) {
final ContentCaptureEvent event = getEvent(events, index, expectedType);
diff --git a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/src/com/android/cts/nolaunchableactivityapp/EmptyService.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivity.java
similarity index 60%
copy from hostsidetests/devicepolicy/app/NoLaunchableActivityApp/src/com/android/cts/nolaunchableactivityapp/EmptyService.java
copy to tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivity.java
index 6cd0da6..ae9134f 100644
--- a/hostsidetests/devicepolicy/app/NoLaunchableActivityApp/src/com/android/cts/nolaunchableactivityapp/EmptyService.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivity.java
@@ -13,20 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.cts.nolaunchableactivityapp;
+package android.contentcaptureservice.cts;
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
+import static android.contentcaptureservice.cts.Assertions.assertNoViewLevelEvents;
-public class EmptyService extends Service {
- public EmptyService() {
- }
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+
+import androidx.annotation.NonNull;
+
+public class BlankActivity extends AbstractContentCaptureActivity {
@Override
- public IBinder onBind(Intent intent) {
- // do nothing, just here for the app to have some code
- throw new UnsupportedOperationException("Not yet implemented");
+ public void assertDefaultEvents(@NonNull Session session) {
+ assertNoViewLevelEvents(session, this);
}
}
-
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivityTest.java
new file mode 100644
index 0000000..6775d56
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankActivityTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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 android.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.CtsContentCaptureService.CONTENT_CAPTURE_SERVICE_COMPONENT_NAME;
+import static android.contentcaptureservice.cts.Helper.resetService;
+
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.DESTROYED;
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.RESUMED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ComponentName;
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+import android.platform.test.annotations.AppModeFull;
+import android.util.Log;
+
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+
+import org.junit.Test;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+@AppModeFull(reason = "BlankWithTitleActivityTest is enough")
+public class BlankActivityTest
+ extends AbstractContentCaptureIntegrationAutoActivityLaunchTest<BlankActivity> {
+
+ private static final String TAG = BlankActivityTest.class.getSimpleName();
+
+ private static final ActivityTestRule<BlankActivity> sActivityRule = new ActivityTestRule<>(
+ BlankActivity.class, false, false);
+
+ public BlankActivityTest() {
+ super(BlankActivity.class);
+ }
+
+ @Override
+ protected ActivityTestRule<BlankActivity> getActivityTestRule() {
+ return sActivityRule;
+ }
+
+ @Test
+ public void testSimpleSessionLifecycle() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final BlankActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ Log.v(TAG, "session id: " + session.id);
+
+ activity.assertDefaultEvents(session);
+ }
+
+ @Test
+ public void testGetServiceComponentName() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ service.waitUntilConnected();
+
+ final ActivityWatcher watcher = startWatcher();
+
+ final BlankActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ try {
+ assertThat(activity.getContentCaptureManager().getServiceComponentName())
+ .isEqualTo(CONTENT_CAPTURE_SERVICE_COMPONENT_NAME);
+
+ resetService();
+ service.waitUntilDisconnected();
+
+ assertThat(activity.getContentCaptureManager().getServiceComponentName())
+ .isNotEqualTo(CONTENT_CAPTURE_SERVICE_COMPONENT_NAME);
+ } finally {
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+ }
+ }
+
+ @Test
+ public void testGetServiceComponentName_onUiThread() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ service.waitUntilConnected();
+
+ final ActivityWatcher watcher = startWatcher();
+
+ final BlankActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ final AtomicReference<ComponentName> ref = new AtomicReference<>();
+ activity.syncRunOnUiThread(
+ () -> ref.set(activity.getContentCaptureManager().getServiceComponentName()));
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ assertThat(ref.get()).isEqualTo(CONTENT_CAPTURE_SERVICE_COMPONENT_NAME);
+ }
+
+ @Test
+ public void testIsContentCaptureFeatureEnabled_onUiThread() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ service.waitUntilConnected();
+
+ final ActivityWatcher watcher = startWatcher();
+
+ final BlankActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ final AtomicBoolean ref = new AtomicBoolean();
+ activity.syncRunOnUiThread(() -> ref
+ .set(activity.getContentCaptureManager().isContentCaptureFeatureEnabled()));
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ assertThat(ref.get()).isTrue();
+ }
+
+ @Test
+ public void testDisableContentCaptureService_onUiThread() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ service.waitUntilConnected();
+
+ final ActivityWatcher watcher = startWatcher();
+
+ final BlankActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ service.disableSelf();
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+ }
+
+ @Test
+ public void testOnConnectionEvents() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ service.waitUntilConnected();
+
+ resetService();
+ service.waitUntilDisconnected();
+ }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankWithTitleActivity.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankWithTitleActivity.java
new file mode 100644
index 0000000..81198f7
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankWithTitleActivity.java
@@ -0,0 +1,70 @@
+/*
+ * 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 android.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Assertions.assertDecorViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertRightActivity;
+import static android.contentcaptureservice.cts.Assertions.assertSessionPaused;
+import static android.contentcaptureservice.cts.Assertions.assertSessionResumed;
+import static android.contentcaptureservice.cts.Assertions.assertViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeFinished;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeStarted;
+import static android.contentcaptureservice.cts.Assertions.assertViewsOptionallyDisappeared;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+import android.util.Log;
+import android.view.View;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureSessionId;
+import android.view.contentcapture.ViewNode;
+
+import androidx.annotation.NonNull;
+
+import java.util.List;
+
+public class BlankWithTitleActivity extends AbstractContentCaptureActivity {
+
+ private static final String TAG = BlankWithTitleActivity.class.getSimpleName();
+
+ @Override
+ public void assertDefaultEvents(@NonNull Session session) {
+ final ContentCaptureSessionId sessionId = session.id;
+ assertRightActivity(session, sessionId, this);
+
+ final View decorView = getDecorView();
+
+ final List<ContentCaptureEvent> events = session.getEvents();
+ Log.v(TAG, "events(" + events.size() + "): " + events);
+
+ final int minEvents = 9; // TODO(b/122315042): disappeared not always sent
+ assertThat(events.size()).isAtLeast(minEvents);
+
+ assertSessionResumed(events, 0);
+ assertViewTreeStarted(events, 1);
+ assertDecorViewAppeared(events, 2, decorView);
+ // TODO(b/123540067): ignoring 3 intermediate parents
+ final ViewNode title = assertViewAppeared(events, 6).getViewNode();
+ assertThat(title.getText()).isEqualTo("Blanka");
+ assertViewTreeFinished(events, 7);
+ assertSessionPaused(events, 8);
+ if (false) { // TODO(b/123540067): disabled because it includes the parent
+ assertViewsOptionallyDisappeared(events, minEvents, decorView.getAutofillId(),
+ title.getAutofillId());
+ }
+ }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankWithTitleActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankWithTitleActivityTest.java
new file mode 100644
index 0000000..c36e036
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankWithTitleActivityTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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 android.contentcaptureservice.cts;
+
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.DESTROYED;
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.RESUMED;
+
+import android.content.Intent;
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+import android.platform.test.annotations.AppModeFull;
+import android.util.Log;
+
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+
+import org.junit.Test;
+
+public class BlankWithTitleActivityTest
+ extends AbstractContentCaptureIntegrationAutoActivityLaunchTest<BlankWithTitleActivity> {
+
+ private static final String TAG = BlankWithTitleActivityTest.class.getSimpleName();
+
+ private static final ActivityTestRule<BlankWithTitleActivity> sActivityRule =
+ new ActivityTestRule<>(BlankWithTitleActivity.class, false, false);
+
+ public BlankWithTitleActivityTest() {
+ super(BlankWithTitleActivity.class);
+ }
+
+ @Override
+ protected ActivityTestRule<BlankWithTitleActivity> getActivityTestRule() {
+ return sActivityRule;
+ }
+
+ @Test
+ public void testSimpleSessionLifecycle() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final BlankWithTitleActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ Log.v(TAG, "session id: " + session.id);
+
+ activity.assertDefaultEvents(session);
+ }
+
+ @AppModeFull(reason = "testSimpleSessionLifecycle() is enough")
+ @Test
+ public void testSimpleSessionLifecycle_noAnimation() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final BlankWithTitleActivity activity = launchActivity(
+ (intent) -> intent.addFlags(
+ Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NEW_TASK));
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ Log.v(TAG, "session id: " + session.id);
+
+ activity.assertDefaultEvents(session);
+ }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivity.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivity.java
new file mode 100644
index 0000000..be6fd85
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivity.java
@@ -0,0 +1,38 @@
+/*
+ * 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 android.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Assertions.assertNoViewLevelEvents;
+
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+
+public class ChildlessActivity extends AbstractRootViewActivity {
+
+ @Override
+ protected void setContentViewOnCreate(Bundle savedInstanceState) {
+ setContentView(R.layout.childless_activity);
+ }
+
+ @Override
+ public void assertDefaultEvents(@NonNull Session session) {
+ // Should be empty because the root view is not important for content capture without a
+ // child that is important.
+ assertNoViewLevelEvents(session, this);
+ }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java
new file mode 100644
index 0000000..9f453b8
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java
@@ -0,0 +1,1261 @@
+/*
+ * 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 android.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Assertions.LifecycleOrder.CREATION;
+import static android.contentcaptureservice.cts.Assertions.LifecycleOrder.DESTRUCTION;
+import static android.contentcaptureservice.cts.Assertions.assertChildSessionContext;
+import static android.contentcaptureservice.cts.Assertions.assertDecorViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertLifecycleOrder;
+import static android.contentcaptureservice.cts.Assertions.assertMainSessionContext;
+import static android.contentcaptureservice.cts.Assertions.assertNoViewLevelEvents;
+import static android.contentcaptureservice.cts.Assertions.assertRightActivity;
+import static android.contentcaptureservice.cts.Assertions.assertSessionPaused;
+import static android.contentcaptureservice.cts.Assertions.assertSessionResumed;
+import static android.contentcaptureservice.cts.Assertions.assertViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertViewDisappeared;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeFinished;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeStarted;
+import static android.contentcaptureservice.cts.Assertions.assertViewsDisappeared;
+import static android.contentcaptureservice.cts.Helper.newImportantView;
+import static android.contentcaptureservice.cts.Helper.sContext;
+
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.DESTROYED;
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.RESUMED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.content.ComponentName;
+import android.content.LocusId;
+import android.contentcaptureservice.cts.CtsContentCaptureService.DisconnectListener;
+import android.contentcaptureservice.cts.CtsContentCaptureService.ServiceWatcher;
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+import android.os.SystemClock;
+import android.platform.test.annotations.AppModeFull;
+import android.util.Log;
+import android.view.View;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ContentCaptureContext;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.ContentCaptureSession;
+import android.view.contentcapture.ContentCaptureSessionId;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+import com.android.compatibility.common.util.ActivityLauncher;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+@AppModeFull(reason = "BlankWithTitleActivityTest is enough")
+public class ChildlessActivityTest
+ extends AbstractContentCaptureIntegrationAutoActivityLaunchTest<ChildlessActivity> {
+
+ private static final String TAG = ChildlessActivityTest.class.getSimpleName();
+
+ private static final ActivityTestRule<ChildlessActivity> sActivityRule = new ActivityTestRule<>(
+ ChildlessActivity.class, false, false);
+
+ public ChildlessActivityTest() {
+ super(ChildlessActivity.class);
+ }
+
+ @Override
+ protected ActivityTestRule<ChildlessActivity> getActivityTestRule() {
+ return sActivityRule;
+ }
+
+ @Before
+ @After
+ public void resetActivityStaticState() {
+ ChildlessActivity.onRootView(null);
+ }
+
+ @Test
+ public void testDefaultLifecycle() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final ChildlessActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ Log.v(TAG, "session id: " + session.id);
+
+ activity.assertDefaultEvents(session);
+ }
+
+ @Test
+ public void testGetContentCapture_disabledWhenNoService() throws Exception {
+ final ActivityWatcher watcher = startWatcher();
+
+ final ChildlessActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ assertThat(activity.getContentCaptureManager().isContentCaptureEnabled()).isFalse();
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+ }
+
+ @Test
+ public void testGetContentCapture_enabledWhenNoService() throws Exception {
+ enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final ChildlessActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ assertThat(activity.getContentCaptureManager().isContentCaptureEnabled()).isTrue();
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ }
+
+ @Test
+ public void testLaunchAnotherActivity() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher1 = startWatcher();
+
+ // Launch and finish 1st activity
+ final ChildlessActivity activity1 = launchActivity();
+ watcher1.waitFor(RESUMED);
+ activity1.finish();
+ watcher1.waitFor(DESTROYED);
+
+ // Launch and finish 2nd activity
+ final ActivityLauncher<LoginActivity> anotherActivityLauncher = new ActivityLauncher<>(
+ sContext, mActivitiesWatcher, LoginActivity.class);
+ final ActivityWatcher watcher2 = anotherActivityLauncher.getWatcher();
+ final LoginActivity activity2 = anotherActivityLauncher.launchActivity();
+ watcher2.waitFor(RESUMED);
+ activity2.finish();
+ watcher2.waitFor(DESTROYED);
+
+ // Assert the sessions
+ final List<ContentCaptureSessionId> sessionIds = service.getAllSessionIds();
+ assertThat(sessionIds).hasSize(2);
+ final ContentCaptureSessionId sessionId1 = sessionIds.get(0);
+ Log.v(TAG, "session id1: " + sessionId1);
+ final ContentCaptureSessionId sessionId2 = sessionIds.get(1);
+ Log.v(TAG, "session id2: " + sessionId2);
+
+ final Session session1 = service.getFinishedSession(sessionId1);
+ activity1.assertDefaultEvents(session1);
+
+ final Session session2 = service.getFinishedSession(sessionId2);
+ activity2.assertDefaultEvents(session2);
+ }
+
+
+ @Test
+ public void testLaunchAnotherActivity_onTopOfIt() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher1 = startWatcher();
+
+ // Launch 1st activity
+ final ChildlessActivity activity1 = launchActivity();
+ watcher1.waitFor(RESUMED);
+
+ // Launch and finish 2nd activity
+ final ActivityLauncher<LoginActivity> anotherActivityLauncher = new ActivityLauncher<>(
+ sContext, mActivitiesWatcher, LoginActivity.class);
+ final ActivityWatcher watcher2 = anotherActivityLauncher.getWatcher();
+ final LoginActivity activity2 = anotherActivityLauncher.launchActivity();
+
+ watcher2.waitFor(RESUMED);
+ activity2.finish();
+ watcher2.waitFor(DESTROYED);
+
+ // Finish 1st activity
+ activity1.finish();
+ watcher1.waitFor(DESTROYED);
+
+ // Assert the activity lifecycle events
+ final ComponentName name1 = activity1.getComponentName();
+ final ComponentName name2 = activity2.getComponentName();
+ service.assertThat()
+ .activityResumed(name1)
+ .activityPaused(name1)
+ .activityResumed(name2)
+ .activityStopped(name1)
+ .activityPaused(name2)
+ .activityResumed(name1)
+ .activityDestroyed(name2)
+ .activityPaused(name1);
+
+ // Assert the sessions
+ final List<ContentCaptureSessionId> sessionIds = service.getAllSessionIds();
+ assertThat(sessionIds).hasSize(2);
+ final ContentCaptureSessionId sessionId1 = sessionIds.get(0);
+ Log.v(TAG, "session id1: " + sessionId1);
+ final ContentCaptureSessionId sessionId2 = sessionIds.get(1);
+ Log.v(TAG, "session id2: " + sessionId2);
+
+ final Session session1 = service.getFinishedSession(sessionId1);
+ final List<ContentCaptureEvent> events1 = session1.getEvents();
+ Log.v(TAG, "events on " + activity1 + ": " + events1);
+ assertThat(events1).hasSize(4);
+ assertSessionResumed(events1, 0);
+ assertSessionPaused(events1, 1);
+ assertSessionResumed(events1, 2);
+ assertSessionPaused(events1, 3);
+
+ final Session session2 = service.getFinishedSession(sessionId2);
+ activity2.assertDefaultEvents(session2);
+
+ }
+
+ @Test
+ public void testAddAndRemoveNoImportantChild() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ // Child must be created inside the lambda because it needs to use the Activity context.
+ final AtomicReference<TextView> childRef = new AtomicReference<>();
+
+ ChildlessActivity.onRootView((activity, rootView) -> {
+ final TextView child = new TextView(activity);
+ child.setText("VIEW, Y U NO IMPORTANT?");
+ child.setImportantForContentCapture(View.IMPORTANT_FOR_CONTENT_CAPTURE_NO);
+
+ rootView.addView(child);
+ });
+
+ final ChildlessActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ // Remove view
+ final TextView child = childRef.get();
+ activity.syncRunOnUiThread(() -> activity.getRootView().removeView(child));
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ final ContentCaptureSessionId sessionId = session.id;
+ Log.v(TAG, "session id: " + sessionId);
+
+ assertRightActivity(session, sessionId, activity);
+
+ // Should be empty because the root view is not important for content capture without a
+ // child that is important.
+ assertNoViewLevelEvents(session, activity);
+ }
+
+ @Test
+ public void testAddAndRemoveImportantChild() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ // TODO(b/120494182): Child must be created inside the lambda because it needs to use the
+ // Activity context.
+ final AtomicReference<TextView> childRef = new AtomicReference<>();
+
+ ChildlessActivity.onRootView((activity, rootView) -> {
+ final TextView text = newImportantView(activity, "Important I am");
+ rootView.addView(text);
+ childRef.set(text);
+ });
+
+ final ChildlessActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ // Remove view
+ final LinearLayout rootView = activity.getRootView();
+ final TextView child = childRef.get();
+ activity.syncRunOnUiThread(() -> rootView.removeView(child));
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ final ContentCaptureSessionId sessionId = session.id;
+ Log.v(TAG, "session id: " + sessionId);
+
+ assertRightActivity(session, sessionId, activity);
+
+ final List<ContentCaptureEvent> events = session.getEvents();
+ Log.v(TAG, "events(" + events.size() + "): " + events);
+
+ final AutofillId rootId = rootView.getAutofillId();
+
+ final View grandpa1 = activity.getGrandParent();
+ final View grandpa2 = activity.getGrandGrandParent();
+ final View decorView = activity.getDecorView();
+
+ // Assert just the relevant events
+ assertThat(events.size()).isAtLeast(12);
+ assertSessionResumed(events, 0);
+ assertViewTreeStarted(events, 1);
+ assertDecorViewAppeared(events, 2, decorView);
+ assertViewAppeared(events, 3, grandpa2, decorView.getAutofillId());
+ assertViewAppeared(events, 4, grandpa1, grandpa2.getAutofillId());
+ assertViewAppeared(events, 5, sessionId, rootView, grandpa1.getAutofillId());
+ assertViewAppeared(events, 6, sessionId, child, rootId);
+ assertViewTreeFinished(events, 7);
+ assertViewTreeStarted(events, 8);
+ assertViewDisappeared(events, 9, child.getAutofillId());
+ assertViewTreeFinished(events, 10);
+ assertSessionPaused(events, 11);
+
+ // TODO(b/122315042): assert parents disappeared
+ }
+
+ @Test
+ public void testAddImportantChildAfterSessionStarted() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final ChildlessActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ // Add View
+ final LinearLayout rootView = activity.getRootView();
+ final TextView child = newImportantView(activity, "Important I am");
+ activity.runOnUiThread(() -> rootView.addView(child));
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ final ContentCaptureSessionId sessionId = session.id;
+ Log.v(TAG, "session id: " + sessionId);
+
+ assertRightActivity(session, sessionId, activity);
+
+ final List<ContentCaptureEvent> events = session.getEvents();
+ Log.v(TAG, "events(" + events.size() + "): " + events);
+
+ final View grandpa = activity.getGrandParent();
+
+ // Assert just the relevant events
+
+ assertThat(events.size()).isAtLeast(6);
+ // TODO(b/122959591): figure out the child is coming first
+ assertSessionResumed(events, 0);
+ assertViewTreeStarted(events, 1);
+ assertViewAppeared(events, 2, sessionId, child, rootView.getAutofillId());
+ assertViewAppeared(events, 3, sessionId, rootView, grandpa.getAutofillId());
+ assertViewTreeFinished(events, 4);
+ assertSessionPaused(events, 5);
+ }
+
+ @Test
+ public void testAddAndRemoveImportantChildOnDifferentSession() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final ChildlessActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ final LinearLayout rootView = activity.getRootView();
+ final View grandpa = activity.getGrandParent();
+
+ final ContentCaptureSession mainSession = rootView.getContentCaptureSession();
+ final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId();
+ Log.v(TAG, "main session id: " + mainSessionId);
+
+ final ContentCaptureSession childSession = mainSession
+ .createContentCaptureSession(newContentCaptureContextBuilder("child")
+ .build());
+ final ContentCaptureSessionId childSessionId = childSession.getContentCaptureSessionId();
+ Log.v(TAG, "child session id: " + childSessionId);
+
+ final TextView child = newImportantView(activity, "Important I am");
+ final AutofillId childId = child.getAutofillId();
+ Log.v(TAG, "childId: " + childId);
+ child.setContentCaptureSession(childSession);
+ activity.runOnUiThread(() -> rootView.addView(child));
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final List<ContentCaptureSessionId> sessionIds = service.getAllSessionIds();
+ assertThat(sessionIds).containsExactly(mainSessionId, childSessionId).inOrder();
+
+ // Assert sessions
+ final Session mainTestSession = service.getFinishedSession(mainSessionId);
+ assertMainSessionContext(mainTestSession, activity);
+ final List<ContentCaptureEvent> mainEvents = mainTestSession.getEvents();
+ Log.v(TAG, "mainEvents(" + mainEvents.size() + "): " + mainEvents);
+
+ assertThat(mainEvents.size()).isAtLeast(5);
+ assertSessionResumed(mainEvents, 0);
+ assertViewTreeStarted(mainEvents, 1);
+ assertViewAppeared(mainEvents, 2, mainSessionId, rootView, grandpa.getAutofillId());
+ assertViewTreeFinished(mainEvents, 3);
+ assertSessionPaused(mainEvents, 4);
+
+ final Session childTestSession = service.getFinishedSession(childSessionId);
+ assertChildSessionContext(childTestSession, "child");
+ final List<ContentCaptureEvent> childEvents = childTestSession.getEvents();
+ Log.v(TAG, "childEvents(" + childEvents.size() + "): " + childEvents);
+ final int minEvents = 3;
+ assertThat(childEvents.size()).isAtLeast(minEvents);
+ assertViewTreeStarted(childEvents, 0);
+ assertViewAppeared(childEvents, 1, childSessionId, child, rootView.getAutofillId());
+ assertViewTreeFinished(childEvents, 2);
+ // TODO(b/122315042): assert parents disappeared
+ }
+
+ /**
+ * Tests scenario where new sessions are added from the main session, but they're not nested
+ * neither have views attached to them.
+ */
+ @Test
+ public void testDinamicallyManageChildlessSiblingSessions() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final ChildlessActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ final ContentCaptureSession mainSession = activity.getRootView().getContentCaptureSession();
+ final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId();
+ Log.v(TAG, "main session id: " + mainSessionId);
+
+ // Create 1st session
+ final ContentCaptureContext context1 = newContentCaptureContextBuilder("session1")
+ .build();
+ final ContentCaptureSession childSession1 = mainSession
+ .createContentCaptureSession(context1);
+ final ContentCaptureSessionId childSessionId1 = childSession1.getContentCaptureSessionId();
+ Log.v(TAG, "child session id 1: " + childSessionId1);
+
+ // Create 2nd session
+ final ContentCaptureContext context2 = newContentCaptureContextBuilder("session2")
+ .build();
+ final ContentCaptureSession childSession2 = mainSession
+ .createContentCaptureSession(context2);
+ final ContentCaptureSessionId childSessionId2 = childSession2.getContentCaptureSessionId();
+ Log.v(TAG, "child session id 2: " + childSessionId2);
+
+ // Close 1st session before opening 3rd
+ childSession1.close();
+
+ // Create 3nd session...
+ final ContentCaptureContext context3 = newContentCaptureContextBuilder("session3")
+ .build();
+ final ContentCaptureSession childSession3 = mainSession
+ .createContentCaptureSession(context3);
+ final ContentCaptureSessionId childSessionId3 = childSession3.getContentCaptureSessionId();
+ Log.v(TAG, "child session id 3: " + childSessionId3);
+
+ // ...and close it right away
+ childSession3.close();
+
+ // Create 4nd session
+ final ContentCaptureContext context4 = newContentCaptureContextBuilder("session4")
+ .build();
+ final ContentCaptureSession childSession4 = mainSession
+ .createContentCaptureSession(context4);
+ final ContentCaptureSessionId childSessionId4 = childSession4.getContentCaptureSessionId();
+ Log.v(TAG, "child session id 4: " + childSessionId4);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds();
+ assertThat(receivedIds).containsExactly(
+ mainSessionId,
+ childSessionId1,
+ childSessionId2,
+ childSessionId3,
+ childSessionId4)
+ .inOrder();
+
+ // Assert main sessions info
+ final Session mainTestSession = service.getFinishedSession(mainSessionId);
+ assertMainSessionContext(mainTestSession, activity);
+
+ final Session childTestSession1 = service.getFinishedSession(childSessionId1);
+ assertChildSessionContext(childTestSession1, "session1");
+
+ final Session childTestSession2 = service.getFinishedSession(childSessionId2);
+ assertChildSessionContext(childTestSession2, "session2");
+
+ final Session childTestSession3 = service.getFinishedSession(childSessionId3);
+ assertChildSessionContext(childTestSession3, "session3");
+
+ final Session childTestSession4 = service.getFinishedSession(childSessionId4);
+ assertChildSessionContext(childTestSession4, "session4");
+
+ // Gets all events first so they're all logged before the assertions
+ final List<ContentCaptureEvent> mainEvents = mainTestSession.getEvents();
+ final List<ContentCaptureEvent> events1 = childTestSession1.getEvents();
+ final List<ContentCaptureEvent> events2 = childTestSession2.getEvents();
+ final List<ContentCaptureEvent> events3 = childTestSession3.getEvents();
+ final List<ContentCaptureEvent> events4 = childTestSession4.getEvents();
+ Log.v(TAG, "mainEvents(" + mainEvents.size() + "): " + mainEvents);
+ Log.v(TAG, "events1(" + events1.size() + "): " + events1);
+ Log.v(TAG, "events2(" + events2.size() + "): " + events2);
+ Log.v(TAG, "events3(" + events3.size() + "): " + events3);
+ Log.v(TAG, "events4(" + events4.size() + "): " + events4);
+
+ assertNoViewLevelEvents(mainTestSession, activity);
+ assertThat(events1).isEmpty();
+ assertThat(events2).isEmpty();
+ assertThat(events3).isEmpty();
+ assertThat(events4).isEmpty();
+
+ // Assert lifecycle methods were called in the right order
+ assertLifecycleOrder(1, mainTestSession, CREATION);
+ assertLifecycleOrder(2, childTestSession1, CREATION);
+ assertLifecycleOrder(3, childTestSession2, CREATION);
+ assertLifecycleOrder(4, childTestSession1, DESTRUCTION);
+ assertLifecycleOrder(5, childTestSession3, CREATION);
+ assertLifecycleOrder(6, childTestSession3, DESTRUCTION);
+ assertLifecycleOrder(7, childTestSession4, CREATION);
+ assertLifecycleOrder(8, childTestSession2, DESTRUCTION);
+ assertLifecycleOrder(9, childTestSession4, DESTRUCTION);
+ assertLifecycleOrder(10, mainTestSession, DESTRUCTION);
+ }
+
+ @Test
+ public void testDinamicallyAddOneChildOnAnotherSession_manuallyCloseSession() throws Exception {
+ dinamicallyAddOneChildOnAnotherSessionTest(/* manuallyCloseSession= */ true);
+ }
+
+ @Test
+ public void testDinamicallyAddOneChildOnAnotherSession_autoCloseSession() throws Exception {
+ dinamicallyAddOneChildOnAnotherSessionTest(/* manuallyCloseSession= */ false);
+ }
+
+ /**
+ * Tests scenario where just 1 session with 1 dinamically added view is created.
+ */
+ private void dinamicallyAddOneChildOnAnotherSessionTest(boolean manuallyCloseSession)
+ throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final ChildlessActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+ final ContentCaptureSession mainSession = activity.getRootView().getContentCaptureSession();
+ final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId();
+ Log.v(TAG, "main session id: " + mainSessionId);
+
+ // Create session
+ final ContentCaptureSession childSession = mainSession
+ .createContentCaptureSession(
+ newContentCaptureContextBuilder("child_session").build());
+ final ContentCaptureSessionId childSessionId = childSession.getContentCaptureSessionId();
+ Log.v(TAG, "child session: " + childSessionId);
+
+ final TextView child = addChild(activity, childSession, "Sweet O'Mine");
+ if (manuallyCloseSession) {
+ waitAndClose(childSession);
+ }
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds();
+ assertThat(receivedIds).containsExactly(mainSessionId, childSessionId).inOrder();
+
+ // Assert main session
+ final Session mainTestSession = service.getFinishedSession(mainSessionId);
+ assertMainSessionContext(mainTestSession, activity);
+ // TODO(b/123540067): ideally it should be empty, but has intermediate parents stuff...
+ // assertThat(mainTestSession.getEvents()).isEmpty();
+
+ // Assert child session
+ final Session childTestSession = service.getFinishedSession(childSessionId);
+ assertChildSessionContext(childTestSession, "child_session");
+ final List<ContentCaptureEvent> childEvents = childTestSession.getEvents();
+ assertThat(childEvents.size()).isAtLeast(3);
+ final AutofillId rootId = activity.getRootView().getAutofillId();
+ assertViewTreeStarted(childEvents, 0);
+ assertViewAppeared(childEvents, 1, child, rootId);
+ assertViewTreeFinished(childEvents, 2);
+
+ // Assert lifecycle methods were called in the right order
+ assertLifecycleOrder(1, mainTestSession, CREATION);
+ assertLifecycleOrder(2, childTestSession, CREATION);
+ assertLifecycleOrder(3, childTestSession, DESTRUCTION);
+ assertLifecycleOrder(4, mainTestSession, DESTRUCTION);
+ }
+
+ /**
+ * Tests scenario where new sessions with children are added from the main session.
+ */
+ @Test
+ public void testDinamicallyManageSiblingSessions() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final ChildlessActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+ final LinearLayout rootView = activity.getRootView();
+ final ContentCaptureSession mainSession = rootView.getContentCaptureSession();
+ final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId();
+ Log.v(TAG, "main session id: " + mainSessionId);
+
+ // Create 1st session
+ final ContentCaptureContext context1 = newContentCaptureContextBuilder("session1")
+ .build();
+ final ContentCaptureSession childSession1 = mainSession
+ .createContentCaptureSession(context1);
+ final ContentCaptureSessionId childSessionId1 = childSession1.getContentCaptureSessionId();
+ Log.v(TAG, "child session id 1: " + childSessionId1);
+
+ // Session 1, child 1
+ final TextView s1c1 = addChild(activity, childSession1, "s1c1");
+
+ // Create 2nd session
+ final ContentCaptureContext context2 = newContentCaptureContextBuilder("session2")
+ .build();
+ final ContentCaptureSession childSession2 = mainSession
+ .createContentCaptureSession(context2);
+ final ContentCaptureSessionId childSessionId2 = childSession2.getContentCaptureSessionId();
+ Log.v(TAG, "child session id 2: " + childSessionId2);
+
+ final TextView s2c1 = newImportantView(activity, childSession2, "s2c1");
+ final TextView s2c2 = newImportantView(activity, childSession2, "s2c1");
+
+ // Add 2 children together so they're wrapped a view_tree batch
+ activity.runOnUiThread(() -> {
+ rootView.addView(s2c1);
+ rootView.addView(s2c2);
+ });
+
+ // Close 1st session before opening 3rd
+ waitAndClose(childSession1);
+
+ // Create 3nd session...
+ final ContentCaptureContext context3 = newContentCaptureContextBuilder("session3")
+ .build();
+ final ContentCaptureSession childSession3 = mainSession
+ .createContentCaptureSession(context3);
+ final ContentCaptureSessionId childSessionId3 = childSession3.getContentCaptureSessionId();
+ Log.v(TAG, "child session id 3: " + childSessionId3);
+
+ final TextView s3c1 = newImportantView(activity, childSession3, "s3c1");
+ final TextView s3c2 = newImportantView(activity, childSession3, "s3c1");
+ final TextView s3c3 = newImportantView(activity, childSession3, "s3c3");
+
+ // Add 2 children together so they're wrapped a view_tree batch
+ activity.runOnUiThread(() -> {
+ rootView.addView(s3c1);
+ rootView.addView(s3c2);
+ });
+
+ // TODO(b/123024698): need to wait until the 4 events are flushed - ideally we should block
+ // waiting until the service received them
+ sleep();
+
+ // Add 2 children so they're wrapped a view_tree batch
+ activity.runOnUiThread(() -> {
+ rootView.removeView(s3c1);
+ rootView.addView(s3c3);
+ });
+
+ // ...and close it right away
+ waitAndClose(childSession3);
+
+ // Create 4nd session
+ final ContentCaptureContext context4 = newContentCaptureContextBuilder("session4")
+ .build();
+ final ContentCaptureSession childSession4 = mainSession
+ .createContentCaptureSession(context4);
+ final ContentCaptureSessionId childSessionId4 = childSession4.getContentCaptureSessionId();
+ Log.v(TAG, "child session id 4: " + childSessionId4);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds();
+ assertThat(receivedIds).containsExactly(
+ mainSessionId,
+ childSessionId1,
+ childSessionId2,
+ childSessionId3,
+ childSessionId4)
+ .inOrder();
+
+ // Assert main sessions info
+ final Session mainTestSession = service.getFinishedSession(mainSessionId);
+ assertMainSessionContext(mainTestSession, activity);
+ final List<ContentCaptureEvent> mainEvents = mainTestSession.getEvents();
+ Log.v(TAG, "main session events(" + mainEvents.size() + "): " + mainEvents);
+
+ // Gets all events first so they're all logged before the assertions
+ final Session childTestSession1 = service.getFinishedSession(childSessionId1);
+ assertChildSessionContext(childTestSession1, "session1");
+ final List<ContentCaptureEvent> events1 = childTestSession1.getEvents();
+ Log.v(TAG, "events1(" + events1.size() + "): " + events1);
+
+ final Session childTestSession2 = service.getFinishedSession(childSessionId2);
+ final List<ContentCaptureEvent> events2 = childTestSession2.getEvents();
+ assertChildSessionContext(childTestSession2, "session2");
+ Log.v(TAG, "events2(" + events2.size() + "): " + events2);
+ final Session childTestSession3 = service.getFinishedSession(childSessionId3);
+ assertChildSessionContext(childTestSession3, "session3");
+ List<ContentCaptureEvent> events3 = childTestSession3.getEvents();
+ Log.v(TAG, "events3(" + events3.size() + "): " + events3);
+
+ final AutofillId rootId = rootView.getAutofillId();
+ final View grandpa = activity.getGrandParent();
+
+ assertThat(mainEvents).hasSize(8);
+ assertSessionResumed(mainEvents, 0);
+ assertViewTreeStarted(mainEvents, 1);
+ assertViewAppeared(mainEvents, 2, rootView, grandpa.getAutofillId());
+ assertViewTreeFinished(mainEvents, 3);
+ assertSessionPaused(mainEvents, 4); // TODO(b/122959591): investigate why
+ assertViewTreeStarted(mainEvents, 5);
+ assertViewDisappeared(mainEvents, 6, rootId);
+ assertViewTreeFinished(mainEvents, 7);
+
+ assertThat(events1).hasSize(3);
+ assertViewTreeStarted(events1, 0);
+ assertViewAppeared(events1, 1, s1c1, rootId);
+ assertViewTreeFinished(events1, 2);
+
+ assertThat(events2.size()).isAtLeast(4);
+ assertViewTreeStarted(events2, 0);
+ assertViewAppeared(events2, 1, s2c1, rootId);
+ assertViewAppeared(events2, 2, s2c2, rootId);
+ assertViewTreeFinished(events2, 3);
+ // TODO(b/122315042): assert parents disappeared
+
+ assertThat(events3).hasSize(8);
+ assertViewTreeStarted(events3, 0);
+ assertViewAppeared(events3, 1, s3c1, rootId);
+ assertViewAppeared(events3, 2, s3c2, rootId);
+ assertViewTreeFinished(events3, 3);
+ assertViewTreeStarted(events3, 4);
+ assertViewDisappeared(events3, 5, s3c1.getAutofillId());
+ assertViewAppeared(events3, 6, s3c3, rootId);
+ assertViewTreeFinished(events3, 7);
+
+ final Session childTestSession4 = service.getFinishedSession(childSessionId4);
+ assertChildSessionContext(childTestSession4, "session4");
+ assertThat(childTestSession4.getEvents()).isEmpty();
+
+ // Assert lifecycle methods were called in the right order
+ assertLifecycleOrder(1, mainTestSession, CREATION);
+ assertLifecycleOrder(2, childTestSession1, CREATION);
+ assertLifecycleOrder(3, childTestSession2, CREATION);
+ assertLifecycleOrder(4, childTestSession1, DESTRUCTION);
+ assertLifecycleOrder(5, childTestSession3, CREATION);
+ assertLifecycleOrder(6, childTestSession3, DESTRUCTION);
+ assertLifecycleOrder(7, childTestSession4, CREATION);
+ assertLifecycleOrder(8, childTestSession2, DESTRUCTION);
+ assertLifecycleOrder(9, childTestSession4, DESTRUCTION);
+ assertLifecycleOrder(10, mainTestSession, DESTRUCTION);
+ }
+
+ @Test
+ public void testNestedSessions_simplestScenario() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final ChildlessActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ final ContentCaptureSession mainSession = activity.getRootView().getContentCaptureSession();
+ final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId();
+ Log.v(TAG, "main session id: " + mainSessionId);
+
+ // Create child session
+ final ContentCaptureContext childContext = newContentCaptureContextBuilder("child")
+ .build();
+ final ContentCaptureSession childSession = mainSession
+ .createContentCaptureSession(childContext);
+ final ContentCaptureSessionId childSessionId = childSession.getContentCaptureSessionId();
+ Log.v(TAG, "child session id: " + childSessionId);
+
+ // Create grand child session
+ final ContentCaptureContext grandChild = newContentCaptureContextBuilder("grandChild")
+ .build();
+ final ContentCaptureSession grandChildSession = childSession
+ .createContentCaptureSession(grandChild);
+ final ContentCaptureSessionId grandChildSessionId = grandChildSession
+ .getContentCaptureSessionId();
+ Log.v(TAG, "child session id: " + grandChildSessionId);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds();
+ assertThat(receivedIds).containsExactly(
+ mainSessionId,
+ childSessionId,
+ grandChildSessionId)
+ .inOrder();
+
+ // Assert sessions
+ final Session mainTestSession = service.getFinishedSession(mainSessionId);
+ assertMainSessionContext(mainTestSession, activity);
+ assertNoViewLevelEvents(mainTestSession, activity);
+
+ final Session childTestSession = service.getFinishedSession(childSessionId);
+ assertChildSessionContext(childTestSession, "child");
+ assertThat(childTestSession.getEvents()).isEmpty();
+
+ final Session grandChildTestSession = service.getFinishedSession(grandChildSessionId);
+ assertChildSessionContext(grandChildTestSession, "grandChild");
+ assertThat(grandChildTestSession.getEvents()).isEmpty();
+
+ // Assert lifecycle methods were called in the right order
+ assertLifecycleOrder(1, mainTestSession, CREATION);
+ assertLifecycleOrder(2, childTestSession, CREATION);
+ assertLifecycleOrder(3, grandChildTestSession, CREATION);
+ assertLifecycleOrder(4, grandChildTestSession, DESTRUCTION);
+ assertLifecycleOrder(5, childTestSession, DESTRUCTION);
+ assertLifecycleOrder(6, mainTestSession, DESTRUCTION);
+ }
+
+ /**
+ * Tests scenario where new sessions are added from each other session, but they're not nested
+ * neither have views attached to them.
+ *
+ * <p>This test actions are exactly the same as
+ * {@link #testDinamicallyManageChildlessSiblingSessions()}, except for session nesting (and
+ * order of lifecycle events).
+ */
+ @Test
+ public void testDinamicallyManageChildlessNestedSessions() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final ChildlessActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ final ContentCaptureSession mainSession = activity.getRootView().getContentCaptureSession();
+ final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId();
+ Log.v(TAG, "main session id: " + mainSessionId);
+
+ // Create 1st session
+ final ContentCaptureContext context1 = newContentCaptureContextBuilder("session1")
+ .build();
+ final ContentCaptureSession childSession1 = mainSession
+ .createContentCaptureSession(context1);
+ final ContentCaptureSessionId childSessionId1 = childSession1.getContentCaptureSessionId();
+ Log.v(TAG, "child session id 1: " + childSessionId1);
+
+ // Create 2nd session
+ final ContentCaptureContext context2 = newContentCaptureContextBuilder("session2")
+ .build();
+ final ContentCaptureSession childSession2 = childSession1
+ .createContentCaptureSession(context2);
+ final ContentCaptureSessionId childSessionId2 = childSession2.getContentCaptureSessionId();
+ Log.v(TAG, "child session id 2: " + childSessionId2);
+
+ // Close 1st session before opening 3rd
+ childSession1.close();
+
+ // Create 3nd session...
+ final ContentCaptureContext context3 = newContentCaptureContextBuilder("session3")
+ .build();
+ final ContentCaptureSession childSession3 = mainSession
+ .createContentCaptureSession(context3);
+ final ContentCaptureSessionId childSessionId3 = childSession3.getContentCaptureSessionId();
+ Log.v(TAG, "child session id 3: " + childSessionId3);
+
+ // ...and close it right away
+ childSession3.close();
+
+ // Create 4nd session
+ final ContentCaptureContext context4 = newContentCaptureContextBuilder("session4")
+ .build();
+ final ContentCaptureSession childSession4 = mainSession
+ .createContentCaptureSession(context4);
+ final ContentCaptureSessionId childSessionId4 = childSession4.getContentCaptureSessionId();
+ Log.v(TAG, "child session id 4: " + childSessionId4);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds();
+ assertThat(receivedIds).containsExactly(
+ mainSessionId,
+ childSessionId1,
+ childSessionId2,
+ childSessionId3,
+ childSessionId4)
+ .inOrder();
+
+ // Assert main sessions info
+ final Session mainTestSession = service.getFinishedSession(mainSessionId);
+ assertMainSessionContext(mainTestSession, activity);
+ assertNoViewLevelEvents(mainTestSession, activity);
+
+ final Session childTestSession1 = service.getFinishedSession(childSessionId1);
+ assertChildSessionContext(childTestSession1, "session1");
+ assertThat(childTestSession1.getEvents()).isEmpty();
+
+ final Session childTestSession2 = service.getFinishedSession(childSessionId2);
+ assertChildSessionContext(childTestSession2, "session2");
+ assertThat(childTestSession2.getEvents()).isEmpty();
+
+ final Session childTestSession3 = service.getFinishedSession(childSessionId3);
+ assertChildSessionContext(childTestSession3, "session3");
+ assertThat(childTestSession3.getEvents()).isEmpty();
+
+ final Session childTestSession4 = service.getFinishedSession(childSessionId4);
+ assertChildSessionContext(childTestSession4, "session4");
+ assertThat(childTestSession4.getEvents()).isEmpty();
+
+ // Assert lifecycle methods were called in the right order
+ assertLifecycleOrder(1, mainTestSession, CREATION);
+ assertLifecycleOrder(2, childTestSession1, CREATION);
+ assertLifecycleOrder(3, childTestSession2, CREATION);
+ assertLifecycleOrder(4, childTestSession2, DESTRUCTION);
+ assertLifecycleOrder(5, childTestSession1, DESTRUCTION);
+ assertLifecycleOrder(6, childTestSession3, CREATION);
+ assertLifecycleOrder(7, childTestSession3, DESTRUCTION);
+ assertLifecycleOrder(8, childTestSession4, CREATION);
+ assertLifecycleOrder(9, childTestSession4, DESTRUCTION);
+ assertLifecycleOrder(10, mainTestSession, DESTRUCTION);
+ }
+
+ /**
+ * Tests scenario where views from different session are removed in sequence - they should not
+ * have been batched.
+ */
+ @Test
+ public void testRemoveChildrenFromDifferentSessions() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final ChildlessActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+ final LinearLayout rootView = activity.getRootView();
+ final ContentCaptureSession mainSession = rootView.getContentCaptureSession();
+ final ContentCaptureSessionId mainSessionId = mainSession.getContentCaptureSessionId();
+ Log.v(TAG, "main session id: " + mainSessionId);
+
+ // Create 1st session
+ final ContentCaptureContext context1 = newContentCaptureContextBuilder("session1")
+ .build();
+ final ContentCaptureSession childSession1 = mainSession
+ .createContentCaptureSession(context1);
+ final ContentCaptureSessionId childSessionId1 = childSession1.getContentCaptureSessionId();
+ Log.v(TAG, "child session id 1: " + childSessionId1);
+
+ // Session 1, child 1
+ final TextView s1c1 = addChild(activity, childSession1, "s1c1");
+ final AutofillId s1c1Id = s1c1.getAutofillId();
+ Log.v(TAG, "childrens from session1: " + s1c1Id);
+
+ // Create 2nd session
+ final ContentCaptureContext context2 = newContentCaptureContextBuilder("session2")
+ .build();
+ final ContentCaptureSession childSession2 = mainSession
+ .createContentCaptureSession(context2);
+ final ContentCaptureSessionId childSessionId2 = childSession2.getContentCaptureSessionId();
+ Log.v(TAG, "child session id 2: " + childSessionId2);
+
+ final TextView s2c1 = newImportantView(activity, childSession2, "s2c1");
+ final AutofillId s2c1Id = s2c1.getAutofillId();
+ final TextView s2c2 = newImportantView(activity, childSession2, "s2c2");
+ final AutofillId s2c2Id = s2c2.getAutofillId();
+ Log.v(TAG, "childrens from session2: " + s2c1Id + ", " + s2c2Id);
+
+ // Add 2 children together so they're wrapped a view_tree batch
+ activity.syncRunOnUiThread(() -> {
+ rootView.addView(s2c1);
+ rootView.addView(s2c2);
+ });
+
+ // Remove views - should generate one batch event for s2 and one single event for s1
+ waitAndRemoveViews(activity, s2c1, s2c2, s1c1);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final List<ContentCaptureSessionId> receivedIds = service.getAllSessionIds();
+ assertThat(receivedIds).containsExactly(
+ mainSessionId,
+ childSessionId1,
+ childSessionId2)
+ .inOrder();
+
+ // Assert main sessions info
+ final Session mainTestSession = service.getFinishedSession(mainSessionId);
+ assertMainSessionContext(mainTestSession, activity);
+ final List<ContentCaptureEvent> mainEvents = mainTestSession.getEvents();
+ Log.v(TAG, "mainEvents(" + mainEvents.size() + "): " + mainEvents);
+
+ // Logs events before asserting
+ final Session childTestSession1 = service.getFinishedSession(childSessionId1);
+ assertChildSessionContext(childTestSession1, "session1");
+ final List<ContentCaptureEvent> events1 = childTestSession1.getEvents();
+ Log.v(TAG, "events1(" + events1.size() + "): " + events1);
+ final Session childTestSession2 = service.getFinishedSession(childSessionId2);
+ final List<ContentCaptureEvent> events2 = childTestSession2.getEvents();
+ assertChildSessionContext(childTestSession2, "session2");
+ Log.v(TAG, "events2(" + events2.size() + "): " + events2);
+
+ // Assert children
+ assertThat(events1.size()).isAtLeast(6);
+ final AutofillId rootId = rootView.getAutofillId();
+ assertViewTreeStarted(events1, 0);
+ assertViewAppeared(events1, 1, s1c1, rootId);
+ assertViewTreeFinished(events1, 2);
+ assertViewTreeStarted(events1, 3);
+ assertViewDisappeared(events1, 4, s1c1Id);
+ assertViewTreeFinished(events1, 5);
+
+ assertThat(events2.size()).isAtLeast(7);
+ assertViewTreeStarted(events2, 0);
+ assertViewAppeared(events2, 1, s2c1, rootId);
+ assertViewAppeared(events2, 2, s2c2, rootId);
+ assertViewTreeFinished(events2, 3);
+ assertViewTreeStarted(events2, 4);
+ assertViewsDisappeared(events2, 5, s2c1Id, s2c2Id);
+ assertViewTreeFinished(events2, 6);
+ }
+
+ /* TODO(b/119638528): add more scenarios for nested sessions, such as:
+ * - add views to the children sessions
+ * - s1 -> s2 -> s3 and main -> s4; close(s1) then generate events on view from s3
+ * - s1 -> s2 -> s3 and main -> s4; close(s2) then generate events on view from s3
+ * - s1 -> s2 and s3->s4 -> s4
+ * - etc
+ */
+
+ private enum DisabledReason {
+ BY_API,
+ BY_SETTINGS,
+ BY_DEVICE_CONFIG
+ }
+
+ private void setFeatureEnabled(@NonNull CtsContentCaptureService service,
+ @NonNull DisabledReason reason,
+ boolean enabled) {
+ switch (reason) {
+ case BY_API:
+ if (enabled) {
+ // The service cannot re-enable itself, so we use settings instead.
+ setFeatureEnabledBySettings(true);
+ } else {
+ service.disableSelf();
+ }
+ break;
+ case BY_SETTINGS:
+ setFeatureEnabledBySettings(enabled);
+ break;
+ case BY_DEVICE_CONFIG:
+ setFeatureEnabledByDeviceConfig(Boolean.toString(enabled));
+ break;
+ default:
+ throw new IllegalArgumentException("invalid reason: " + reason);
+ }
+ }
+
+ @Test
+ public void testIsContentCaptureFeatureEnabled_notService() throws Exception {
+ final ContentCaptureManager mgr = getContentCaptureManagerHack();
+ assertThrows(SecurityException.class, () -> mgr.isContentCaptureFeatureEnabled());
+ }
+
+ @Test
+ public void testSetContentCaptureFeatureEnabled_disabledBySettings() throws Exception {
+ setContentCaptureFeatureEnabledTest_disabled(DisabledReason.BY_SETTINGS);
+ }
+
+ private void setContentCaptureFeatureEnabledTest_disabled(@NonNull DisabledReason reason)
+ throws Exception {
+ final ContentCaptureManager mgr = getContentCaptureManagerHack();
+
+ final CtsContentCaptureService service = enableService();
+ assertThat(mgr.isContentCaptureFeatureEnabled()).isTrue();
+ final DisconnectListener disconnectedListener = service.setOnDisconnectListener();
+
+ setFeatureEnabled(service, reason, /* enabled= */ false);
+
+ disconnectedListener.waitForOnDisconnected();
+ assertThat(mgr.isContentCaptureFeatureEnabled()).isFalse();
+ assertThat(mgr.isContentCaptureEnabled()).isFalse();
+
+ final ActivityWatcher watcher = startWatcher();
+ final ChildlessActivity activity = launchActivity();
+
+ watcher.waitFor(RESUMED);
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ assertThat(service.getAllSessionIds()).isEmpty();
+ }
+
+ @Test
+ public void testSetContentCaptureFeatureEnabled_disabledThenReEnabledBySettings()
+ throws Exception {
+ setContentCaptureFeatureEnabledTest_disabledThenReEnabled(DisabledReason.BY_SETTINGS);
+ }
+
+ private void setContentCaptureFeatureEnabledTest_disabledThenReEnabled(
+ @NonNull DisabledReason reason) throws Exception {
+ final ContentCaptureManager mgr = getContentCaptureManagerHack();
+
+ final CtsContentCaptureService service1 = enableService();
+ assertThat(mgr.isContentCaptureFeatureEnabled()).isTrue();
+ final DisconnectListener disconnectedListener = service1.setOnDisconnectListener();
+
+ setFeatureEnabled(service1, reason, /* enabled= */ false);
+ disconnectedListener.waitForOnDisconnected();
+
+ assertThat(mgr.isContentCaptureFeatureEnabled()).isFalse();
+ assertThat(mgr.isContentCaptureEnabled()).isFalse();
+
+ // Launch and finish 1st activity while it's disabled
+ final ActivityWatcher watcher1 = startWatcher();
+ final ChildlessActivity activity1 = launchActivity();
+ watcher1.waitFor(RESUMED);
+ activity1.finish();
+ watcher1.waitFor(DESTROYED);
+
+ // Re-enable feature
+ final ServiceWatcher reconnectionWatcher = CtsContentCaptureService.setServiceWatcher();
+ reconnectionWatcher.whitelistSelf();
+ setFeatureEnabled(service1, reason, /* enabled= */ true);
+ final CtsContentCaptureService service2 = reconnectionWatcher.waitOnCreate();
+ assertThat(mgr.isContentCaptureFeatureEnabled()).isTrue();
+
+ // Launch and finish 2nd activity while it's enabled
+ final ActivityLauncher<CustomViewActivity> launcher2 = new ActivityLauncher<>(
+ sContext, mActivitiesWatcher, CustomViewActivity.class);
+ final ActivityWatcher watcher2 = launcher2.getWatcher();
+ final CustomViewActivity activity2 = launcher2.launchActivity();
+ watcher2.waitFor(RESUMED);
+ activity2.finish();
+ watcher2.waitFor(DESTROYED);
+
+ assertThat(service1.getAllSessionIds()).isEmpty();
+ final Session session = service2.getOnlyFinishedSession();
+ activity2.assertDefaultEvents(session);
+ }
+
+ @Test
+ public void testSetContentCaptureFeatureEnabled_disabledByApi() throws Exception {
+ setContentCaptureFeatureEnabledTest_disabled(DisabledReason.BY_API);
+ }
+
+ @Test
+ public void testSetContentCaptureFeatureEnabled_disabledThenReEnabledByApi()
+ throws Exception {
+ setContentCaptureFeatureEnabledTest_disabledThenReEnabled(DisabledReason.BY_API);
+ }
+
+ @Test
+ public void testSetContentCaptureFeatureEnabled_disabledByDeviceConfig() throws Exception {
+ setContentCaptureFeatureEnabledTest_disabled(DisabledReason.BY_DEVICE_CONFIG);
+ // Reset service, otherwise it will reconnect when the deviceConfig value is reset
+ // on cleanup, which will cause the test to fail
+ Helper.resetService();
+ }
+
+ @Test
+ public void testSetContentCaptureFeatureEnabled_disabledThenReEnabledByDeviceConfig()
+ throws Exception {
+ setContentCaptureFeatureEnabledTest_disabledThenReEnabled(DisabledReason.BY_DEVICE_CONFIG);
+ // Reset service, otherwise it will reconnect when the deviceConfig value is reset
+ // on cleanup, which will cause the test to fail
+ Helper.resetService();
+ }
+
+ // TODO(b/123406031): add tests that mix feature_enabled with user_restriction_enabled (and
+ // make sure mgr.isContentCaptureFeatureEnabled() returns only the state of the 1st)
+
+ private TextView addChild(@NonNull ChildlessActivity activity,
+ @NonNull ContentCaptureSession session, @NonNull String text) {
+ final TextView child = newImportantView(activity, text);
+ child.setContentCaptureSession(session);
+ Log.i(TAG, "adding " + child.getAutofillId() + " on session "
+ + session.getContentCaptureSessionId());
+ activity.runOnUiThread(() -> activity.getRootView().addView(child));
+ return child;
+ }
+
+ // TODO(b/123024698): these method are used in cases where we cannot close a session because we
+ // would miss intermediate events, so we need to sleep. This is a hack (it's slow and flaky):
+ // ideally we should block and wait until the service receives the event, but right now
+ // we don't get the service events until after the activity is finished, so we cannot do that...
+ private void waitAndClose(@NonNull ContentCaptureSession session) {
+ Log.d(TAG, "sleeping before closing " + session.getContentCaptureSessionId());
+ sleep();
+ session.close();
+ }
+
+ private void waitAndRemoveViews(@NonNull ChildlessActivity activity, @NonNull View... views) {
+ Log.d(TAG, "sleeping before removing " + Arrays.toString(views));
+ sleep();
+ activity.syncRunOnUiThread(() -> {
+ for (View view : views) {
+ activity.getRootView().removeView(view);
+ }
+ });
+ }
+
+ private void sleep() {
+ Log.d(TAG, "sleeping for 1s ");
+ SystemClock.sleep(1_000);
+ }
+
+ // TODO(b/120494182): temporary hack to get the manager, which currently is only available on
+ // Activity contexts (and would be null from sContext)
+ @NonNull
+ private ContentCaptureManager getContentCaptureManagerHack() throws InterruptedException {
+ final AtomicReference<ContentCaptureManager> ref = new AtomicReference<>();
+ LoginActivity.onRootView(
+ (activity, rootView) -> ref.set(activity.getContentCaptureManager()));
+
+ final ActivityLauncher<LoginActivity> launcher = new ActivityLauncher<>(
+ sContext, mActivitiesWatcher, LoginActivity.class);
+ final ActivityWatcher watcher = launcher.getWatcher();
+ final LoginActivity activity = launcher.launchActivity();
+ watcher.waitFor(RESUMED);
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final ContentCaptureManager mgr = ref.get();
+ assertThat(mgr).isNotNull();
+
+ return mgr;
+ }
+
+ private void setFeatureEnabledByDeviceConfig(@Nullable String value) {
+ Log.d(TAG, "setFeatureEnabledByDeviceConfig(): " + value);
+
+ sKillSwitchManager.set(value);
+ }
+
+ @NonNull
+ private ContentCaptureContext.Builder newContentCaptureContextBuilder(@NonNull String id) {
+ return new ContentCaptureContext.Builder(new LocusId(id));
+ }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java
index e01837c..349ff36 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CtsContentCaptureService.java
@@ -25,6 +25,7 @@
import android.service.contentcapture.ActivityEvent;
import android.service.contentcapture.ContentCaptureService;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.view.contentcapture.ContentCaptureContext;
@@ -103,7 +104,7 @@
// but that would make the tests flaker.
/**
- * Used for testing onDataRemovalRequest.
+ * Used for testing onUserDataRemovalRequest.
*/
private DataRemovalRequest mRemovalRequest;
@@ -281,7 +282,7 @@
@Override
public void onDataRemovalRequest(DataRemovalRequest request) {
- Log.i(TAG, "onDataRemovalRequest(id=" + mId + ",req=" + request + ")");
+ Log.i(TAG, "onUserDataRemovalRequest(id=" + mId + ",req=" + request + ")");
mRemovalRequest = request;
}
@@ -292,7 +293,7 @@
}
/**
- * Gets the cached DataRemovalRequest for testing.
+ * Gets the cached UserDataRemovalRequest for testing.
*/
public DataRemovalRequest getRemovalRequest() {
return mRemovalRequest;
@@ -503,12 +504,21 @@
}
/**
- * Whitelist stuff when the service connects.
+ * Whitelists stuff when the service connects.
*/
public void whitelist(@Nullable Pair<Set<String>, Set<ComponentName>> whitelist) {
mWhitelist = whitelist;
}
+ /**
+ * Whitelists just this package.
+ */
+ public void whitelistSelf() {
+ final ArraySet<String> pkgs = new ArraySet<>(1);
+ pkgs.add(MY_PACKAGE);
+ whitelist(new Pair<>(pkgs, null));
+ }
+
@Override
public String toString() {
return "mService: " + mService + " created: " + (mCreated.getCount() == 0)
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomView.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomView.java
index 1da0502..10b054b 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomView.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomView.java
@@ -21,14 +21,14 @@
import android.util.Log;
import android.view.View;
import android.view.ViewStructure;
-import android.view.contentcapture.ContentCaptureSession;
import androidx.annotation.NonNull;
import com.android.compatibility.common.util.Visitor;
/**
- * A view that can be used to emulate custom behavior (like virtual children)
+ * A view that can be used to emulate custom behavior (like virtual children) on
+ * {@link #onProvideContentCaptureStructure(ViewStructure, int)}.
*/
public class CustomView extends View {
@@ -38,37 +38,29 @@
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
+ setImportantForContentCapture(View.IMPORTANT_FOR_CONTENT_CAPTURE_YES);
}
- public void onProvideContentCaptureStructure(@NonNull ViewStructure structure) {
- Log.v(TAG, "onProvideContentCaptureStructure(): delegate=" + mDelegate);
+ @Override
+ public void onProvideContentCaptureStructure(ViewStructure structure, int flags) {
if (mDelegate != null) {
Log.d(TAG, "onProvideContentCaptureStructure(): delegating");
structure.setClassName(getAccessibilityClassName().toString());
mDelegate.visit(structure);
Log.d(TAG, "onProvideContentCaptureStructure(): delegated");
- }
- else {
- Log.d(TAG, "onProvideContentCaptureStructure(): explicitly setting class name");
- structure.setClassName(getAccessibilityClassName().toString());
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (changed) {
- final ContentCaptureSession session = getContentCaptureSession();
- final ViewStructure structure = session.newViewStructure(this);
- onProvideContentCaptureStructure(structure);
- session.notifyViewAppeared(structure);
+ } else {
+ superOnProvideContentCaptureStructure(structure, flags);
}
}
@Override
public CharSequence getAccessibilityClassName() {
- final String name = CustomView.class.getName();
- Log.d(TAG, "getAccessibilityClassName(): " + name);
- return name;
+ return CustomView.class.getName();
+ }
+
+ void superOnProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
+ Log.d(TAG, "calling super.onProvideContentCaptureStructure()");
+ super.onProvideContentCaptureStructure(structure, flags);
}
void setContentCaptureDelegate(@NonNull Visitor<ViewStructure> delegate) {
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivity.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivity.java
index ad1051c..bae47e5 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivity.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivity.java
@@ -15,9 +15,13 @@
*/
package android.contentcaptureservice.cts;
+import static android.contentcaptureservice.cts.Assertions.assertDecorViewAppeared;
import static android.contentcaptureservice.cts.Assertions.assertRightActivity;
import static android.contentcaptureservice.cts.Assertions.assertSessionPaused;
import static android.contentcaptureservice.cts.Assertions.assertSessionResumed;
+import static android.contentcaptureservice.cts.Assertions.assertViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeFinished;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeStarted;
import static android.contentcaptureservice.cts.Assertions.assertViewWithUnknownParentAppeared;
import static android.contentcaptureservice.cts.Assertions.assertViewsOptionallyDisappeared;
@@ -50,13 +54,13 @@
* <p>Used on {@link #assertInitialViewsAppeared(Session, int)} and
* {@link #assertInitialViewsDisappeared(List, int)}.
*/
- public static final int MIN_EVENTS = 2;
+ public static final int MIN_EVENTS = 7;
CustomView mCustomView;
/**
* Sets a delegate that provides the behavior of
- * {@link CustomView#onProvideContentCaptureStructure(ViewStructure)}.
+ * {@link CustomView#onProvideContentCaptureStructure(ViewStructure, int)}.
*/
static void setCustomViewDelegate(@NonNull DoubleVisitor<CustomView, ViewStructure> delegate) {
sCustomViewDelegate = delegate;
@@ -126,7 +130,12 @@
// Assert just the relevant events
assertSessionResumed(events, 0);
- assertViewWithUnknownParentAppeared(events, 1, session.id, mCustomView);
+ assertViewTreeStarted(events, 1);
+ assertDecorViewAppeared(events, 2, getDecorView());
+ assertViewAppeared(events, 3, grandpa2, decorView.getAutofillId());
+ assertViewAppeared(events, 4, grandpa1, grandpa2.getAutofillId());
+ assertViewWithUnknownParentAppeared(events, 5, session.id, mCustomView);
+ assertViewTreeFinished(events, 6);
return events;
}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
index f96c3d8..e01ed72 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
@@ -15,10 +15,14 @@
*/
package android.contentcaptureservice.cts;
+import static android.contentcaptureservice.cts.Assertions.assertDecorViewAppeared;
import static android.contentcaptureservice.cts.Assertions.assertRightActivity;
import static android.contentcaptureservice.cts.Assertions.assertSessionPaused;
import static android.contentcaptureservice.cts.Assertions.assertSessionResumed;
+import static android.contentcaptureservice.cts.Assertions.assertViewAppeared;
import static android.contentcaptureservice.cts.Assertions.assertViewTextChanged;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeFinished;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeStarted;
import static android.contentcaptureservice.cts.Assertions.assertViewWithUnknownParentAppeared;
import static android.contentcaptureservice.cts.Assertions.assertVirtualViewAppeared;
import static android.contentcaptureservice.cts.Assertions.assertVirtualViewDisappeared;
@@ -160,15 +164,20 @@
// Assert just the relevant events
assertSessionResumed(events, 0);
+ assertViewTreeStarted(events, 1);
+ assertDecorViewAppeared(events, 2, decorView);
+ assertViewAppeared(events, 3, grandpa2, decorView.getAutofillId());
+ assertViewAppeared(events, 4, grandpa1, grandpa2.getAutofillId());
final ContentCaptureSession mainSession = activity.mCustomView.getContentCaptureSession();
- assertVirtualViewAppeared(events, 1, mainSession, customViewId, 1, "child");
- assertVirtualViewDisappeared(events, 2, customViewId, mainSession, 1);
+ assertVirtualViewAppeared(events, 5, mainSession, customViewId, 1, "child");
+ assertVirtualViewDisappeared(events, 6, customViewId, mainSession, 1);
// This is the "wrong" part - the parent is notified last
- assertViewWithUnknownParentAppeared(events, 3, session.id, activity.mCustomView);
+ assertViewWithUnknownParentAppeared(events, 7, session.id, activity.mCustomView);
- assertSessionPaused(events, 4);
+ assertViewTreeFinished(events, 8);
+ assertSessionPaused(events, 9);
activity.assertInitialViewsDisappeared(events, additionalEvents);
}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
index 8e97aac..ed5340c 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
@@ -25,6 +25,7 @@
import android.os.SystemClock;
import android.util.ArraySet;
import android.util.Log;
+import android.view.View;
import android.view.contentcapture.ContentCaptureSession;
import android.widget.TextView;
@@ -118,6 +119,7 @@
public static TextView newImportantView(@NonNull Context context, @NonNull String text) {
final TextView child = new TextView(context);
child.setText(text);
+ child.setImportantForContentCapture(View.IMPORTANT_FOR_CONTENT_CAPTURE_YES);
Log.v(TAG, "newImportantView(text=" + text + ", id=" + child.getAutofillId() + ")");
return child;
}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivity.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivity.java
new file mode 100644
index 0000000..3747e2b
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivity.java
@@ -0,0 +1,191 @@
+/*
+ * 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 android.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Assertions.assertDecorViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertRightActivity;
+import static android.contentcaptureservice.cts.Assertions.assertSessionId;
+import static android.contentcaptureservice.cts.Assertions.assertSessionResumed;
+import static android.contentcaptureservice.cts.Assertions.assertViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeFinished;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeStarted;
+import static android.contentcaptureservice.cts.Assertions.assertViewsOptionallyDisappeared;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureSessionId;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import java.util.List;
+
+public class LoginActivity extends AbstractRootViewActivity {
+
+ private static final String TAG = LoginActivity.class.getSimpleName();
+
+ /**
+ * Mininum number of events generated when the activity starts.
+ *
+ * <p>Used on {@link #assertInitialViewsAppeared(Session, int)} and
+ * {@link #assertInitialViewsDisappeared(List, int)}.
+ */
+ public static final int MIN_EVENTS = 11;
+
+ TextView mUsernameLabel;
+ EditText mUsername;
+ TextView mPasswordLabel;
+ EditText mPassword;
+
+ @Override
+ protected void setContentViewOnCreate(Bundle savedInstanceState) {
+ setContentView(R.layout.login_activity);
+
+ mUsernameLabel = findViewById(R.id.username_label);
+ mUsername = findViewById(R.id.username);
+ mPasswordLabel = findViewById(R.id.password_label);
+ mPassword = findViewById(R.id.password);
+ }
+
+ @Override
+ public void assertDefaultEvents(@NonNull Session session) {
+ final int additionalEvents = 0;
+ final List<ContentCaptureEvent> events = assertInitialViewsAppeared(session,
+ additionalEvents);
+ assertInitialViewsDisappeared(events, additionalEvents);
+ }
+
+ /**
+ * Asserts the events generated when this activity was launched, up to the
+ * {@code TYPE_INITIAL_VIEW_HIERARCHY_FINISHED} event.
+ */
+ @NonNull
+ public List<ContentCaptureEvent> assertInitialViewsAppeared(@NonNull Session session,
+ int additionalEvents) {
+ final List<ContentCaptureEvent> events = assertJustInitialViewsAppeared(session,
+ additionalEvents);
+ assertViewTreeFinished(events, MIN_EVENTS - 1);
+
+ return events;
+ }
+
+ /**
+ * Asserts the events generated when this activity was launched, but without the
+ * {@code TYPE_INITIAL_VIEW_HIERARCHY_FINISHED} event.
+ */
+ @NonNull
+ public List<ContentCaptureEvent> assertJustInitialViewsAppeared(@NonNull Session session,
+ int additionalEvents) {
+ final LoginActivity activity = this;
+ final ContentCaptureSessionId sessionId = session.id;
+ assertRightActivity(session, sessionId, activity);
+
+ // Sanity check
+ assertSessionId(sessionId, activity.mUsernameLabel);
+ assertSessionId(sessionId, activity.mUsername);
+ assertSessionId(sessionId, activity.mPassword);
+ assertSessionId(sessionId, activity.mPasswordLabel);
+
+ final List<ContentCaptureEvent> events = session.getEvents();
+ Log.v(TAG, "events(" + events.size() + "): " + events);
+ // TODO(b/123540067): ideally it should be X so it reflects just the views defined
+ // in the layout - right now it's generating events for 2 intermediate parents
+ // (android:action_mode_bar_stub and android:content), we should try to create an
+ // activity without them
+
+ final AutofillId rootId = activity.getRootView().getAutofillId();
+
+ assertThat(events.size()).isAtLeast(MIN_EVENTS + additionalEvents);
+
+ // TODO(b/123540067): get rid of those intermediated parents
+ final View grandpa1 = activity.getGrandParent();
+ final View grandpa2 = activity.getGrandGrandParent();
+ final View decorView = activity.getDecorView();
+ final View rootView = activity.getRootView();
+
+ assertSessionResumed(events, 0);
+ assertViewTreeStarted(events, 1);
+ assertDecorViewAppeared(events, 2, decorView);
+ assertViewAppeared(events, 3, grandpa2, decorView.getAutofillId());
+ assertViewAppeared(events, 4, grandpa1, grandpa2.getAutofillId());
+ assertViewAppeared(events, 5, sessionId, rootView, grandpa1.getAutofillId());
+ assertViewAppeared(events, 6, sessionId, activity.mUsernameLabel, rootId);
+ assertViewAppeared(events, 7, sessionId, activity.mUsername, rootId);
+ assertViewAppeared(events, 8, sessionId, activity.mPasswordLabel, rootId);
+ assertViewAppeared(events, 9, sessionId, activity.mPassword, rootId);
+
+ return events;
+ }
+
+ /**
+ * Asserts the initial views disappeared after the activity was finished.
+ */
+ public void assertInitialViewsDisappeared(@NonNull List<ContentCaptureEvent> events,
+ int additionalEvents) {
+ // TODO(b/122315042): this method is currently a mess, so let's disable for now and properly
+ // fix these assertions later...
+ if (true) return;
+
+ final LoginActivity activity = this;
+ final AutofillId rootId = activity.getRootView().getAutofillId();
+ final View decorView = activity.getDecorView();
+ final View grandpa1 = activity.getGrandParent();
+ final View grandpa2 = activity.getGrandGrandParent();
+
+ // Besides the additional events from the test case, we also need to account for the
+ final int i = MIN_EVENTS + additionalEvents;
+
+ assertViewTreeStarted(events, i);
+
+ // TODO(b/122315042): sometimes we get decor view disappareared events, sometimes we don't
+ // As we don't really care about those, let's fix it!
+ try {
+ assertViewsOptionallyDisappeared(events, i + 1,
+ rootId,
+ grandpa1.getAutofillId(), grandpa2.getAutofillId(),
+ activity.mUsernameLabel.getAutofillId(), activity.mUsername.getAutofillId(),
+ activity.mPasswordLabel.getAutofillId(), activity.mPassword.getAutofillId());
+ } catch (AssertionError e) {
+ Log.e(TAG, "Hack-ignoring assertion without decor view: " + e);
+ // Try again removing it...
+ assertViewsOptionallyDisappeared(events, i + 1,
+ rootId,
+ grandpa1.getAutofillId(), grandpa2.getAutofillId(),
+ decorView.getAutofillId(),
+ activity.mUsernameLabel.getAutofillId(), activity.mUsername.getAutofillId(),
+ activity.mPasswordLabel.getAutofillId(), activity.mPassword.getAutofillId());
+ }
+
+ assertViewTreeFinished(events, i + 2);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ Log.d(TAG, "AutofillIds: " + "usernameLabel=" + mUsernameLabel.getAutofillId()
+ + ", username=" + mUsername.getAutofillId()
+ + ", passwordLabel=" + mPasswordLabel.getAutofillId()
+ + ", password=" + mPassword.getAutofillId());
+ }
+}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java
new file mode 100644
index 0000000..ea22140
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/LoginActivityTest.java
@@ -0,0 +1,783 @@
+/*
+ * 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 android.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Assertions.assertChildSessionContext;
+import static android.contentcaptureservice.cts.Assertions.assertContextUpdated;
+import static android.contentcaptureservice.cts.Assertions.assertDecorViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertMainSessionContext;
+import static android.contentcaptureservice.cts.Assertions.assertRightActivity;
+import static android.contentcaptureservice.cts.Assertions.assertRightRelationship;
+import static android.contentcaptureservice.cts.Assertions.assertSessionId;
+import static android.contentcaptureservice.cts.Assertions.assertSessionPaused;
+import static android.contentcaptureservice.cts.Assertions.assertSessionResumed;
+import static android.contentcaptureservice.cts.Assertions.assertViewAppeared;
+import static android.contentcaptureservice.cts.Assertions.assertViewTextChanged;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeFinished;
+import static android.contentcaptureservice.cts.Assertions.assertViewTreeStarted;
+import static android.contentcaptureservice.cts.Assertions.assertViewsOptionallyDisappeared;
+import static android.contentcaptureservice.cts.Helper.MY_PACKAGE;
+import static android.contentcaptureservice.cts.Helper.newImportantView;
+import static android.view.contentcapture.DataRemovalRequest.FLAG_IS_PREFIX;
+
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.DESTROYED;
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.RESUMED;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.ComponentName;
+import android.content.LocusId;
+import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+import android.os.Bundle;
+import android.platform.test.annotations.AppModeFull;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ContentCaptureContext;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureSession;
+import android.view.contentcapture.ContentCaptureSessionId;
+import android.view.contentcapture.DataRemovalRequest;
+import android.view.contentcapture.DataRemovalRequest.LocusIdRequest;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+import com.android.compatibility.common.util.DoubleVisitor;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+@AppModeFull(reason = "BlankWithTitleActivityTest is enough")
+public class LoginActivityTest
+ extends AbstractContentCaptureIntegrationAutoActivityLaunchTest<LoginActivity> {
+
+ private static final String TAG = LoginActivityTest.class.getSimpleName();
+
+ private static final int NO_FLAGS = 0;
+
+ private static final ActivityTestRule<LoginActivity> sActivityRule = new ActivityTestRule<>(
+ LoginActivity.class, false, false);
+
+ public LoginActivityTest() {
+ super(LoginActivity.class);
+ }
+
+ @Override
+ protected ActivityTestRule<LoginActivity> getActivityTestRule() {
+ return sActivityRule;
+ }
+
+ @Before
+ @After
+ public void resetActivityStaticState() {
+ LoginActivity.onRootView(null);
+ }
+
+ @Test
+ public void testSimpleLifecycle_defaultSession() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ Log.v(TAG, "session id: " + session.id);
+
+ activity.assertDefaultEvents(session);
+
+ final ComponentName name = activity.getComponentName();
+ service.assertThat()
+ .activityResumed(name)
+ .activityPaused(name);
+ }
+
+ @Test
+ public void testSimpleLifecycle_rootViewSession() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final ContentCaptureContext clientContext = newContentCaptureContext();
+
+ final AtomicReference<ContentCaptureSession> mainSessionRef = new AtomicReference<>();
+ final AtomicReference<ContentCaptureSession> childSessionRef = new AtomicReference<>();
+
+ LoginActivity.onRootView((activity, rootView) -> {
+ final ContentCaptureSession mainSession = rootView.getContentCaptureSession();
+ mainSessionRef.set(mainSession);
+ final ContentCaptureSession childSession = mainSession
+ .createContentCaptureSession(clientContext);
+ childSessionRef.set(childSession);
+ Log.i(TAG, "Setting root view (" + rootView + ") session to " + childSession);
+ rootView.setContentCaptureSession(childSession);
+ });
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final ContentCaptureSessionId mainSessionId = mainSessionRef.get()
+ .getContentCaptureSessionId();
+ final ContentCaptureSessionId childSessionId = childSessionRef.get()
+ .getContentCaptureSessionId();
+ Log.v(TAG, "session ids: main=" + mainSessionId + ", child=" + childSessionId);
+
+ // Sanity checks
+ assertSessionId(childSessionId, activity.getRootView());
+ assertSessionId(childSessionId, activity.mUsernameLabel);
+ assertSessionId(childSessionId, activity.mUsername);
+ assertSessionId(childSessionId, activity.mPassword);
+ assertSessionId(childSessionId, activity.mPasswordLabel);
+
+ // Get the sessions
+ final Session mainSession = service.getFinishedSession(mainSessionId);
+ final Session childSession = service.getFinishedSession(childSessionId);
+
+ assertRightActivity(mainSession, mainSessionId, activity);
+ assertRightRelationship(mainSession, childSession);
+
+ // Sanity check
+ final List<ContentCaptureSessionId> allSessionIds = service.getAllSessionIds();
+ assertThat(allSessionIds).containsExactly(mainSessionId, childSessionId);
+
+ /*
+ * Asserts main session
+ */
+
+ // Checks context
+ assertMainSessionContext(mainSession, activity);
+
+ // Check events
+ final List<ContentCaptureEvent> mainEvents = mainSession.getEvents();
+ Log.v(TAG, "events(" + mainEvents.size() + ") for main session: " + mainEvents);
+
+ final View grandpa1 = activity.getGrandParent();
+ final View grandpa2 = activity.getGrandGrandParent();
+ final View decorView = activity.getDecorView();
+ final AutofillId rootId = activity.getRootView().getAutofillId();
+
+ final int minEvents = 7; // TODO(b/122315042): disappeared not always sent
+ assertThat(mainEvents.size()).isAtLeast(minEvents);
+ assertSessionResumed(mainEvents, 0);
+ assertViewTreeStarted(mainEvents, 1);
+ assertDecorViewAppeared(mainEvents, 2, decorView);
+ assertViewAppeared(mainEvents, 3, grandpa2, decorView.getAutofillId());
+ assertViewAppeared(mainEvents, 4, grandpa1, grandpa2.getAutofillId());
+ assertViewTreeFinished(mainEvents, 5);
+ // TODO(b/122315042): these assertions are currently a mess, so let's disable for now and
+ // properly fix them later...
+ if (false) {
+ int pausedIndex = 6;
+ final boolean disappeared = assertViewsOptionallyDisappeared(mainEvents, pausedIndex,
+ decorView.getAutofillId(),
+ grandpa2.getAutofillId(), grandpa1.getAutofillId());
+ if (disappeared) {
+ pausedIndex += 3;
+ }
+ assertSessionPaused(mainEvents, pausedIndex);
+ }
+
+ /*
+ * Asserts child session
+ */
+
+ // Checks context
+ assertChildSessionContext(childSession, "file://dev/null");
+
+ assertContentCaptureContext(childSession.context);
+
+ // Check events
+ final List<ContentCaptureEvent> childEvents = childSession.getEvents();
+ Log.v(TAG, "events for child session: " + childEvents);
+ final int minChildEvents = 5;
+ assertThat(childEvents.size()).isAtLeast(minChildEvents);
+ assertViewAppeared(childEvents, 0, childSessionId, activity.getRootView(),
+ grandpa1.getAutofillId());
+ assertViewAppeared(childEvents, 1, childSessionId, activity.mUsernameLabel, rootId);
+ assertViewAppeared(childEvents, 2, childSessionId, activity.mUsername, rootId);
+ assertViewAppeared(childEvents, 3, childSessionId, activity.mPasswordLabel, rootId);
+ assertViewAppeared(childEvents, 4, childSessionId, activity.mPassword, rootId);
+
+ assertViewsOptionallyDisappeared(childEvents, minChildEvents,
+ rootId,
+ activity.mUsernameLabel.getAutofillId(), activity.mUsername.getAutofillId(),
+ activity.mPasswordLabel.getAutofillId(), activity.mPassword.getAutofillId());
+ }
+
+ @Test
+ public void testSimpleLifecycle_changeContextAfterCreate() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ final ContentCaptureContext newContext1 = newContentCaptureContext();
+ final ContentCaptureContext newContext2 = null;
+
+ final View rootView = activity.getRootView();
+ final ContentCaptureSession mainSession = rootView.getContentCaptureSession();
+ assertThat(mainSession).isNotNull();
+ Log.i(TAG, "Updating root view (" + rootView + ") context to " + newContext1);
+ mainSession.setContentCaptureContext(newContext1);
+ assertContentCaptureContext(mainSession.getContentCaptureContext());
+
+ Log.i(TAG, "Updating root view (" + rootView + ") context to " + newContext2);
+ mainSession.setContentCaptureContext(newContext2);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ Log.v(TAG, "session id: " + session.id);
+
+ final int additionalEvents = 2;
+ final List<ContentCaptureEvent> events = activity.assertInitialViewsAppeared(session,
+ additionalEvents);
+
+ final ContentCaptureEvent event1 = assertContextUpdated(events, LoginActivity.MIN_EVENTS);
+ final ContentCaptureContext actualContext = event1.getContentCaptureContext();
+ assertContentCaptureContext(actualContext);
+
+ final ContentCaptureEvent event2 = assertContextUpdated(events,
+ LoginActivity.MIN_EVENTS + 1);
+ assertThat(event2.getContentCaptureContext()).isNull();
+ }
+
+ @Test
+ public void testSimpleLifecycle_changeContextOnCreate() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final ContentCaptureContext newContext = newContentCaptureContext();
+
+ LoginActivity.onRootView((activity, rootView) -> {
+ final ContentCaptureSession mainSession = rootView.getContentCaptureSession();
+ Log.i(TAG, "Setting root view (" + rootView + ") context to " + newContext);
+ mainSession.setContentCaptureContext(newContext);
+ assertContentCaptureContext(mainSession.getContentCaptureContext());
+ });
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ Log.v(TAG, "session id: " + session.id);
+ final ContentCaptureSessionId sessionId = session.id;
+ assertRightActivity(session, sessionId, activity);
+
+ // Sanity check
+
+ final List<ContentCaptureEvent> events = session.getEvents();
+ Log.v(TAG, "events(" + events.size() + "): " + events);
+ // TODO(b/123540067): ideally it should be X so it reflects just the views defined
+ // in the layout - right now it's generating events for 2 intermediate parents
+ // (android:action_mode_bar_stub and android:content), we should try to create an
+ // activity without them
+
+ final AutofillId rootId = activity.getRootView().getAutofillId();
+
+ assertThat(events.size()).isAtLeast(11);
+
+ // TODO(b/123540067): get rid of those intermediated parents
+ final View grandpa1 = activity.getGrandParent();
+ final View grandpa2 = activity.getGrandGrandParent();
+ final View decorView = activity.getDecorView();
+ final View rootView = activity.getRootView();
+
+ final ContentCaptureEvent ctxUpdatedEvent = assertContextUpdated(events, 0);
+ final ContentCaptureContext actualContext = ctxUpdatedEvent.getContentCaptureContext();
+ assertContentCaptureContext(actualContext);
+
+ assertSessionResumed(events, 1);
+ assertViewTreeStarted(events, 2);
+ assertDecorViewAppeared(events, 3, decorView);
+ assertViewAppeared(events, 4, grandpa2, decorView.getAutofillId());
+ assertViewAppeared(events, 5, grandpa1, grandpa2.getAutofillId());
+ assertViewAppeared(events, 6, sessionId, rootView, grandpa1.getAutofillId());
+ assertViewAppeared(events, 7, sessionId, activity.mUsernameLabel, rootId);
+ assertViewAppeared(events, 8, sessionId, activity.mUsername, rootId);
+ assertViewAppeared(events, 9, sessionId, activity.mPasswordLabel, rootId);
+ assertViewAppeared(events, 10, sessionId, activity.mPassword, rootId);
+ }
+
+ @Test
+ public void testTextChanged() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ LoginActivity.onRootView((activity, rootView) -> ((LoginActivity) activity).mUsername
+ .setText("user"));
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.syncRunOnUiThread(() -> {
+ activity.mUsername.setText("USER");
+ activity.mPassword.setText("PASS");
+ });
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ final ContentCaptureSessionId sessionId = session.id;
+
+ assertRightActivity(session, sessionId, activity);
+
+ final int additionalEvents = 2;
+ final List<ContentCaptureEvent> events = activity.assertInitialViewsAppeared(session,
+ additionalEvents);
+
+ final int i = LoginActivity.MIN_EVENTS;
+
+ assertViewTextChanged(events, i, activity.mUsername.getAutofillId(), "USER");
+ assertViewTextChanged(events, i + 1, activity.mPassword.getAutofillId(), "PASS");
+
+ activity.assertInitialViewsDisappeared(events, additionalEvents);
+ }
+
+ @Test
+ public void testTextChangeBuffer() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ LoginActivity.onRootView((activity, rootView) -> ((LoginActivity) activity).mUsername
+ .setText(""));
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.syncRunOnUiThread(() -> {
+ activity.mUsername.setText("a");
+ activity.mUsername.setText("ab");
+
+ activity.mPassword.setText("d");
+ activity.mPassword.setText("de");
+
+ activity.mUsername.setText("abc");
+ });
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ final ContentCaptureSessionId sessionId = session.id;
+
+ assertRightActivity(session, sessionId, activity);
+
+ final int additionalEvents = 3;
+ final List<ContentCaptureEvent> events = activity.assertInitialViewsAppeared(session,
+ additionalEvents);
+
+ final int i = LoginActivity.MIN_EVENTS;
+
+ assertViewTextChanged(events, i, activity.mUsername.getAutofillId(), "ab");
+ assertViewTextChanged(events, i + 1, activity.mPassword.getAutofillId(), "de");
+ assertViewTextChanged(events, i + 2, activity.mUsername.getAutofillId(), "abc");
+
+ activity.assertInitialViewsDisappeared(events, additionalEvents);
+ }
+
+ @Test
+ public void testDisabledByFlagSecure() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ LoginActivity.onRootView((activity, rootView) -> activity.getWindow()
+ .addFlags(WindowManager.LayoutParams.FLAG_SECURE));
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ assertThat((session.context.getFlags()
+ & ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE) != 0).isTrue();
+ final ContentCaptureSessionId sessionId = session.id;
+ Log.v(TAG, "session id: " + sessionId);
+
+ assertRightActivity(session, sessionId, activity);
+
+ final List<ContentCaptureEvent> events = session.getEvents();
+ assertThat(events).isEmpty();
+ }
+
+ @Test
+ public void testDisabledByApp() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ LoginActivity.onRootView((activity, rootView) -> activity.getContentCaptureManager()
+ .setContentCaptureEnabled(false));
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ assertThat(activity.getContentCaptureManager().isContentCaptureEnabled()).isFalse();
+
+ activity.syncRunOnUiThread(() -> activity.mUsername.setText("D'OH"));
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ assertThat((session.context.getFlags()
+ & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0).isTrue();
+ final ContentCaptureSessionId sessionId = session.id;
+ Log.v(TAG, "session id: " + sessionId);
+
+ assertRightActivity(session, sessionId, activity);
+
+ final List<ContentCaptureEvent> events = session.getEvents();
+ assertThat(events).isEmpty();
+ }
+
+ @Test
+ public void testDisabledFlagSecureAndByApp() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ LoginActivity.onRootView((activity, rootView) -> {
+ activity.getContentCaptureManager().setContentCaptureEnabled(false);
+ activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
+ });
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ assertThat(activity.getContentCaptureManager().isContentCaptureEnabled()).isFalse();
+ activity.syncRunOnUiThread(() -> activity.mUsername.setText("D'OH"));
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ assertThat((session.context.getFlags()
+ & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0).isTrue();
+ assertThat((session.context.getFlags()
+ & ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE) != 0).isTrue();
+ final ContentCaptureSessionId sessionId = session.id;
+ Log.v(TAG, "session id: " + sessionId);
+
+ assertRightActivity(session, sessionId, activity);
+
+ final List<ContentCaptureEvent> events = session.getEvents();
+ assertThat(events).isEmpty();
+ }
+
+ @Test
+ public void testUserDataRemovalRequest_forEverything() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ LoginActivity.onRootView((activity, rootView) -> activity.getContentCaptureManager()
+ .removeData(new DataRemovalRequest.Builder().forEverything()
+ .build()));
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ DataRemovalRequest request = service.getRemovalRequest();
+ assertThat(request).isNotNull();
+ assertThat(request.isForEverything()).isTrue();
+ assertThat(request.getLocusIdRequests()).isNull();
+ assertThat(request.getPackageName()).isEqualTo(MY_PACKAGE);
+ }
+
+ @Test
+ public void testUserDataRemovalRequest_oneId() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final LocusId locusId = new LocusId("com.example");
+
+ LoginActivity.onRootView((activity, rootView) -> activity.getContentCaptureManager()
+ .removeData(new DataRemovalRequest.Builder()
+ .addLocusId(locusId, NO_FLAGS)
+ .build()));
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ DataRemovalRequest request = service.getRemovalRequest();
+ assertThat(request).isNotNull();
+ assertThat(request.isForEverything()).isFalse();
+ assertThat(request.getPackageName()).isEqualTo(MY_PACKAGE);
+
+ final List<LocusIdRequest> requests = request.getLocusIdRequests();
+ assertThat(requests.size()).isEqualTo(1);
+
+ final LocusIdRequest actualRequest = requests.get(0);
+ assertThat(actualRequest.getLocusId()).isEqualTo(locusId);
+ assertThat(actualRequest.getFlags()).isEqualTo(NO_FLAGS);
+ }
+
+ @Test
+ public void testUserDataRemovalRequest_manyIds() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ final LocusId locusId1 = new LocusId("com.example");
+ final LocusId locusId2 = new LocusId("com.example2");
+
+ LoginActivity.onRootView((activity, rootView) -> activity.getContentCaptureManager()
+ .removeData(new DataRemovalRequest.Builder()
+ .addLocusId(locusId1, NO_FLAGS)
+ .addLocusId(locusId2, FLAG_IS_PREFIX)
+ .build()));
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final DataRemovalRequest request = service.getRemovalRequest();
+ assertThat(request).isNotNull();
+ assertThat(request.isForEverything()).isFalse();
+ assertThat(request.getPackageName()).isEqualTo(MY_PACKAGE);
+
+ final List<LocusIdRequest> requests = request.getLocusIdRequests();
+ assertThat(requests.size()).isEqualTo(2);
+
+ final LocusIdRequest actualRequest1 = requests.get(0);
+ assertThat(actualRequest1.getLocusId()).isEqualTo(locusId1);
+ assertThat(actualRequest1.getFlags()).isEqualTo(NO_FLAGS);
+
+ final LocusIdRequest actualRequest2 = requests.get(1);
+ assertThat(actualRequest2.getLocusId()).isEqualTo(locusId2);
+ assertThat(actualRequest2.getFlags()).isEqualTo(FLAG_IS_PREFIX);
+ }
+
+ @Test
+ public void testAddChildren_rightAway() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+ final View[] children = new View[2];
+
+ final DoubleVisitor<AbstractRootViewActivity, LinearLayout> visitor = (activity,
+ rootView) -> {
+ final TextView child1 = newImportantView(activity, "c1");
+ children[0] = child1;
+ Log.v(TAG, "Adding child1(" + child1.getAutofillId() + "): " + child1);
+ rootView.addView(child1);
+ final TextView child2 = newImportantView(activity, "c1");
+ children[1] = child2;
+ Log.v(TAG, "Adding child2(" + child2.getAutofillId() + "): " + child2);
+ rootView.addView(child2);
+ };
+ LoginActivity.onRootView(visitor);
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ Log.v(TAG, "session id: " + session.id);
+
+ final ContentCaptureSessionId sessionId = session.id;
+ assertRightActivity(session, sessionId, activity);
+
+ final List<ContentCaptureEvent> events = activity.assertJustInitialViewsAppeared(session,
+ /* additionalEvents= */ 2);
+ final AutofillId rootId = activity.getRootView().getAutofillId();
+ int i = LoginActivity.MIN_EVENTS - 1;
+ assertViewAppeared(events, i, sessionId, children[0], rootId);
+ assertViewAppeared(events, i + 1, sessionId, children[1], rootId);
+ assertViewTreeFinished(events, i + 2);
+
+ activity.assertInitialViewsDisappeared(events, children.length);
+ }
+
+ @Test
+ public void testAddChildren_afterAnimation() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+ final View[] children = new View[2];
+
+ final DoubleVisitor<AbstractRootViewActivity, LinearLayout> visitor = (activity,
+ rootView) -> {
+ final TextView child1 = newImportantView(activity, "c1");
+ children[0] = child1;
+ Log.v(TAG, "Adding child1(" + child1.getAutofillId() + "): " + child1);
+ rootView.addView(child1);
+ final TextView child2 = newImportantView(activity, "c1");
+ children[1] = child2;
+ Log.v(TAG, "Adding child2(" + child2.getAutofillId() + "): " + child2);
+ rootView.addView(child2);
+ };
+ LoginActivity.onAnimationComplete(visitor);
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ final Session session = service.getOnlyFinishedSession();
+ Log.v(TAG, "session id: " + session.id);
+
+ final ContentCaptureSessionId sessionId = session.id;
+ assertRightActivity(session, sessionId, activity);
+ final int additionalEvents = 2; // 2 children views
+ final List<ContentCaptureEvent> events = activity.assertJustInitialViewsAppeared(session,
+ additionalEvents);
+ assertThat(events.size()).isAtLeast(LoginActivity.MIN_EVENTS + 5);
+ final View decorView = activity.getDecorView();
+ final View grandpa1 = activity.getGrandParent();
+ final View grandpa2 = activity.getGrandGrandParent();
+ final AutofillId rootId = activity.getRootView().getAutofillId();
+ int i = LoginActivity.MIN_EVENTS - 1;
+
+ assertViewTreeFinished(events, i);
+ assertViewTreeStarted(events, i + 1);
+ assertViewAppeared(events, i + 2, sessionId, children[0], rootId);
+ assertViewAppeared(events, i + 3, sessionId, children[1], rootId);
+ assertViewTreeFinished(events, i + 4);
+
+ // TODO(b/122315042): assert parents disappeared
+ if (true) return;
+
+ // TODO(b/122315042): sometimes we get decor view disappareared events, sometimes we don't
+ // As we don't really care about those, let's fix it!
+ try {
+ assertViewsOptionallyDisappeared(events, LoginActivity.MIN_EVENTS + additionalEvents,
+ rootId,
+ grandpa1.getAutofillId(), grandpa2.getAutofillId(),
+ activity.mUsernameLabel.getAutofillId(), activity.mUsername.getAutofillId(),
+ activity.mPasswordLabel.getAutofillId(), activity.mPassword.getAutofillId(),
+ children[0].getAutofillId(), children[1].getAutofillId());
+ } catch (AssertionError e) {
+ Log.e(TAG, "Hack-ignoring assertion without decor view: " + e);
+ // Try again removing it...
+ assertViewsOptionallyDisappeared(events, LoginActivity.MIN_EVENTS + additionalEvents,
+ rootId,
+ grandpa1.getAutofillId(), grandpa2.getAutofillId(),
+ decorView.getAutofillId(),
+ activity.mUsernameLabel.getAutofillId(), activity.mUsername.getAutofillId(),
+ activity.mPasswordLabel.getAutofillId(), activity.mPassword.getAutofillId(),
+ children[0].getAutofillId(), children[1].getAutofillId());
+
+ }
+ }
+
+ @Test
+ public void testWhitelist_packageNotWhitelisted() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ActivityWatcher watcher = startWatcher();
+
+ service.setContentCaptureWhitelist((Set) null, (Set) null);
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ assertThat(service.getAllSessionIds()).isEmpty();
+ }
+
+ @Test
+ public void testWhitelist_activityNotWhitelisted() throws Exception {
+ final CtsContentCaptureService service = enableService();
+ final ArraySet<ComponentName> components = new ArraySet<>();
+ components.add(new ComponentName(MY_PACKAGE, "some.activity"));
+ service.setContentCaptureWhitelist(null, components);
+ final ActivityWatcher watcher = startWatcher();
+
+ final LoginActivity activity = launchActivity();
+ watcher.waitFor(RESUMED);
+
+ activity.finish();
+ watcher.waitFor(DESTROYED);
+
+ assertThat(service.getAllSessionIds()).isEmpty();
+ }
+
+ /**
+ * Creates a context that can be assert by
+ * {@link #assertContentCaptureContext(ContentCaptureContext)}.
+ */
+ private ContentCaptureContext newContentCaptureContext() {
+ final String id = "file://dev/null";
+ final Bundle bundle = new Bundle();
+ bundle.putString("DUDE", "SWEET");
+ return new ContentCaptureContext.Builder(new LocusId(id)).setExtras(bundle).build();
+ }
+
+ /**
+ * Asserts a context that can has been created by {@link #newContentCaptureContext()}.
+ */
+ private void assertContentCaptureContext(@NonNull ContentCaptureContext context) {
+ assertWithMessage("null context").that(context).isNotNull();
+ assertWithMessage("wrong ID on context %s", context).that(context.getLocusId().getId())
+ .isEqualTo("file://dev/null");
+ final Bundle extras = context.getExtras();
+ assertWithMessage("no extras on context %s", context).that(extras).isNotNull();
+ assertWithMessage("wrong number of extras on context %s", context).that(extras.size())
+ .isEqualTo(1);
+ assertWithMessage("wrong extras on context %s", context).that(extras.getString("DUDE"))
+ .isEqualTo("SWEET");
+ }
+
+ // TODO(b/123540602): add moar test cases for different sessions:
+ // - session1 on rootView, session2 on children
+ // - session1 on rootView, session2 on child1, session3 on child2
+ // - combination above where the CTS test explicitly finishes a session
+
+ // TODO(b/123540602): add moar test cases for different scenarios, like:
+ // - dynamically adding /
+ // - removing views
+ // - pausing / resuming activity / tapping home
+ // - changing text
+ // - secure flag with child sessions
+ // - making sure events are flushed when activity pause / resume
+
+ // TODO(b/126262658): moar lifecycle events, like multiple activities.
+
+}
diff --git a/tests/contentsuggestions/src/android/contentsuggestions/cts/ContentSuggestionsManagerTest.java b/tests/contentsuggestions/src/android/contentsuggestions/cts/ContentSuggestionsManagerTest.java
index c9f7fbb..20c6a5f 100644
--- a/tests/contentsuggestions/src/android/contentsuggestions/cts/ContentSuggestionsManagerTest.java
+++ b/tests/contentsuggestions/src/android/contentsuggestions/cts/ContentSuggestionsManagerTest.java
@@ -37,10 +37,13 @@
import androidx.annotation.NonNull;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.RequiredServiceRule;
+
import com.google.common.collect.Lists;
import org.junit.After;
import org.junit.Before;
+import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,6 +61,10 @@
private static final long VERIFY_TIMEOUT_MS = 5_000;
private static final long SERVICE_LIFECYCLE_TIMEOUT_MS = 10_000;
+ @ClassRule
+ public static final RequiredServiceRule mRequiredServiceRule =
+ new RequiredServiceRule(Context.CONTENT_SUGGESTIONS_SERVICE);
+
private ContentSuggestionsManager mManager;
private CtsContentSuggestionsService.Watcher mWatcher;
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index 4f8fe3d..a674c86 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -272,6 +272,7 @@
</service>
<activity android:name="android.server.wm.AlertWindowsAppOpsTestsActivity"/>
+ <activity android:name="android.server.wm.CloseOnOutsideTestActivity" />
<activity android:name="android.server.wm.DialogFrameTestActivity" />
<activity android:name="android.server.wm.DisplayCutoutTests$TestActivity"
android:turnScreenOn="true"
diff --git a/tests/framework/base/windowmanager/app27/AndroidManifest.xml b/tests/framework/base/windowmanager/app27/AndroidManifest.xml
index ab9dbc0..66da0e6 100755
--- a/tests/framework/base/windowmanager/app27/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/app27/AndroidManifest.xml
@@ -34,6 +34,17 @@
android:exported="true"
/>
+ <activity android:name=".LaunchEnterPipActivity"
+ android:exported="true"
+ />
+
+ <activity android:name=".PipActivity"
+ android:resizeableActivity="false"
+ android:supportsPictureInPicture="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:exported="true"
+ />
+
<activity android:name=".HomeActivity"
android:enabled="false"
android:exported="true">
diff --git a/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/Components.java b/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/Components.java
index fd05e74..bf59582 100644
--- a/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/Components.java
+++ b/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/Components.java
@@ -33,6 +33,12 @@
public static final ComponentName SDK_27_SEPARATE_PROCESS_ACTIVITY =
component(Components.class, "SeparateProcessActivity");
+ public static final ComponentName SDK_27_LAUNCH_ENTER_PIP_ACTIVITY =
+ component(Components.class, "LaunchEnterPipActivity");
+
+ public static final ComponentName SDK_27_PIP_ACTIVITY =
+ component(Components.class, "PipActivity");
+
public static final ComponentName SDK_27_HOME_ACTIVITY =
component(Components.class, "HomeActivity");
}
diff --git a/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/LaunchEnterPipActivity.java b/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/LaunchEnterPipActivity.java
new file mode 100644
index 0000000..ad1e431
--- /dev/null
+++ b/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/LaunchEnterPipActivity.java
@@ -0,0 +1,33 @@
+/*
+ * 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 android.server.wm.app27;
+
+import static android.server.wm.app.Components.PipActivity.EXTRA_ENTER_PIP;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class LaunchEnterPipActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ if (getIntent().hasExtra(EXTRA_ENTER_PIP)) {
+ startActivity(new Intent(this, PipActivity.class));
+ }
+ }
+}
diff --git a/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/PipActivity.java b/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/PipActivity.java
new file mode 100644
index 0000000..6bcf8fa
--- /dev/null
+++ b/tests/framework/base/windowmanager/app27/src/android/server/wm/app27/PipActivity.java
@@ -0,0 +1,33 @@
+/*
+ * 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 android.server.wm.app27;
+
+import android.app.Activity;
+import android.app.PictureInPictureParams;
+import android.os.Bundle;
+import android.util.Rational;
+
+public class PipActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ enterPictureInPictureMode(
+ new PictureInPictureParams.Builder()
+ .setAspectRatio(new Rational(1, 1))
+ .build());
+ }
+}
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CloseOnOutsideTestActivity.java b/tests/framework/base/windowmanager/src/android/server/wm/CloseOnOutsideTestActivity.java
new file mode 100644
index 0000000..5f4a88d
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CloseOnOutsideTestActivity.java
@@ -0,0 +1,46 @@
+/*
+ * 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 android.server.wm;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.WindowManager.LayoutParams;
+
+import androidx.annotation.Nullable;
+
+
+/**
+ * Activity that makes its Window half width/height so that an area exists outside which can be
+ * tapped to close it when {@link Activity#setFinishOnTouchOutside(boolean)} is enabled.
+ */
+public class CloseOnOutsideTestActivity extends Activity {
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+ int width = displayMetrics.widthPixels;
+ int height = displayMetrics.heightPixels;
+
+ LayoutParams params = getWindow().getAttributes();
+ params.width = width / 2;
+ params.height = height / 2;
+ getWindow().setAttributes(params);
+ }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CloseOnOutsideTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CloseOnOutsideTests.java
new file mode 100644
index 0000000..313c5f5
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CloseOnOutsideTests.java
@@ -0,0 +1,79 @@
+/*
+ * 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 android.server.wm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.app.Instrumentation;
+import android.util.DisplayMetrics;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.ShellUtils;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests {@link android.view.Window#setCloseOnTouchOutside(boolean)} through exposed Activity API.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CloseOnOutsideTests {
+
+ @Rule
+ public final ActivityTestRule<CloseOnOutsideTestActivity> mTestActivity =
+ new ActivityTestRule<>(CloseOnOutsideTestActivity.class, true, true);
+
+ @Test
+ public void withDefaults() {
+ touchAndAssert(false /* shouldBeFinishing */);
+ }
+
+ @Test
+ public void finishTrue() {
+ mTestActivity.getActivity().setFinishOnTouchOutside(true);
+ touchAndAssert(true /* shouldBeFinishing */);
+ }
+
+ @Test
+ public void finishFalse() {
+ mTestActivity.getActivity().setFinishOnTouchOutside(false);
+ touchAndAssert(false /* shouldBeFinishing */);
+ }
+
+ // Tap the bottom right and check the Activity is finishing
+ private void touchAndAssert(boolean shouldBeFinishing) {
+ DisplayMetrics displayMetrics =
+ mTestActivity.getActivity().getResources().getDisplayMetrics();
+ int width = (int) (displayMetrics.widthPixels * 0.875f);
+ int height = (int) (displayMetrics.heightPixels * 0.875f);
+
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+ // To be safe, make sure nothing else is finishing the Activity
+ instrumentation.runOnMainSync(() -> assertFalse(mTestActivity.getActivity().isFinishing()));
+
+ ShellUtils.runShellCommand("input tap %d %d", width, height);
+
+ instrumentation.runOnMainSync(
+ () -> assertEquals(shouldBeFinishing, mTestActivity.getActivity().isFinishing()));
+ }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
index e01c51c..98669b7 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -60,6 +60,8 @@
import static android.server.wm.app.Components.TestActivity.EXTRA_CONFIGURATION;
import static android.server.wm.app.Components.TestActivity.EXTRA_FIXED_ORIENTATION;
import static android.server.wm.app.Components.TestActivity.TEST_ACTIVITY_ACTION_FINISH_SELF;
+import static android.server.wm.app27.Components.SDK_27_LAUNCH_ENTER_PIP_ACTIVITY;
+import static android.server.wm.app27.Components.SDK_27_PIP_ACTIVITY;
import static android.view.Display.DEFAULT_DISPLAY;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
@@ -752,6 +754,30 @@
}
@Test
+ public void testLaunchStoppedActivityWithPiPInSameProcessPreQ() {
+ // Try to enter picture-in-picture from an activity that has more than one activity in the
+ // task and ensure that it works, for pre-Q app
+ launchActivity(SDK_27_LAUNCH_ENTER_PIP_ACTIVITY,
+ EXTRA_ENTER_PIP, "true");
+ waitForEnterPip(SDK_27_PIP_ACTIVITY);
+ assertPinnedStackExists();
+
+ // Puts the host activity to stopped state
+ launchHomeActivity();
+ mAmWmState.assertHomeActivityVisible(true);
+ waitAndAssertActivityState(SDK_27_LAUNCH_ENTER_PIP_ACTIVITY, STATE_STOPPED,
+ "Activity should become STOPPED");
+ mAmWmState.assertVisibility(SDK_27_LAUNCH_ENTER_PIP_ACTIVITY, false);
+
+ // Host activity should be visible after re-launch and PiP window still exists
+ launchActivity(SDK_27_LAUNCH_ENTER_PIP_ACTIVITY);
+ waitAndAssertActivityState(SDK_27_LAUNCH_ENTER_PIP_ACTIVITY, STATE_RESUMED,
+ "Activity should become RESUMED");
+ mAmWmState.assertVisibility(SDK_27_LAUNCH_ENTER_PIP_ACTIVITY, true);
+ assertPinnedStackExists();
+ }
+
+ @Test
public void testEnterPipWithResumeWhilePausingActivityNoStop() throws Exception {
/*
* Launch the resumeWhilePausing activity and ensure that the PiP activity did not get
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java
index f73d5a6..2ec92bb 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java
@@ -168,7 +168,7 @@
insets.getMandatorySystemGestureInsets(),
insetsLessThanOrEqualTo(insets.getSystemGestureInsets()));
- Insets stableAndSystem = Insets.max(insets.getSystemGestureInsets(),
+ Insets stableAndSystem = Insets.max(insets.getSystemWindowInsets(),
insets.getStableInsets());
assertThat("mandatory system gesture insets must include intersection between "
+ "stable and system window insets",
diff --git a/tests/tests/location/src/android/location/cts/ScanningSettingsTest.java b/tests/tests/location/src/android/location/cts/ScanningSettingsTest.java
index ed7c582..c455751 100644
--- a/tests/tests/location/src/android/location/cts/ScanningSettingsTest.java
+++ b/tests/tests/location/src/android/location/cts/ScanningSettingsTest.java
@@ -137,4 +137,4 @@
clickAndWaitForSettingChange(pref, resolver, settingKey);
assertEquals(checked, Settings.Global.getInt(resolver, settingKey, 0) == 1);
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/net/jni/NativeMultinetworkJni.cpp b/tests/tests/net/jni/NativeMultinetworkJni.cpp
index 5bd3013..2832c3d 100644
--- a/tests/tests/net/jni/NativeMultinetworkJni.cpp
+++ b/tests/tests/net/jni/NativeMultinetworkJni.cpp
@@ -145,7 +145,7 @@
}
extern "C"
-JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runResNqueryCheck(
+JNIEXPORT void Java_android_net_cts_MultinetworkApiTest_runResNqueryCheck(
JNIEnv* env, jclass, jlong nethandle) {
net_handle_t handle = (net_handle_t) nethandle;
@@ -155,29 +155,15 @@
EXPECT_EQ(env, 0, expectAnswersValid(env, fd, AF_INET, ns_r_noerror),
"v4 res_nquery check answers");
- // V4 NXDOMAIN
- fd = android_res_nquery(handle, kNxDomainName, ns_c_in, ns_t_a, 0);
- EXPECT_GE(env, fd, 0, "v4 res_nquery NXDOMAIN");
- EXPECT_EQ(env, 0, expectAnswersValid(env, fd, AF_INET, ns_r_nxdomain),
- "v4 res_nquery NXDOMAIN check answers");
-
// V6
fd = android_res_nquery(handle, kHostname, ns_c_in, ns_t_aaaa, 0);
EXPECT_GE(env, fd, 0, "v6 res_nquery");
EXPECT_EQ(env, 0, expectAnswersValid(env, fd, AF_INET, ns_r_noerror),
"v6 res_nquery check answers");
-
- // V6 NXDOMAIN
- fd = android_res_nquery(handle, kNxDomainName, ns_c_in, ns_t_aaaa, 0);
- EXPECT_GE(env, fd, 0, "v6 res_nquery NXDOMAIN");
- EXPECT_EQ(env, 0, expectAnswersValid(env, fd, AF_INET, ns_r_nxdomain),
- "v6 res_nquery NXDOMAIN check answers");
-
- return 0;
}
extern "C"
-JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runResNsendCheck(
+JNIEXPORT void Java_android_net_cts_MultinetworkApiTest_runResNsendCheck(
JNIEnv* env, jclass, jlong nethandle) {
net_handle_t handle = (net_handle_t) nethandle;
// V4
@@ -200,15 +186,6 @@
EXPECT_EQ(env, 0, expectAnswersValid(env, fd1, AF_INET, ns_r_noerror),
"v4 res_nsend 1st check answers");
- // V4 NXDOMAIN
- memset(buf1, 0, sizeof(buf1));
- len1 = makeQuery(kNxDomainName, ns_t_a, buf1, sizeof(buf1));
- EXPECT_GT(env, len1, 0, "v4 res_mkquery NXDOMAIN");
- fd1 = android_res_nsend(handle, buf1, len1, 0);
- EXPECT_GE(env, fd1, 0, "v4 res_nsend NXDOMAIN");
- EXPECT_EQ(env, 0, expectAnswersValid(env, fd1, AF_INET, ns_r_nxdomain),
- "v4 res_nsend NXDOMAIN check answers");
-
// V6
memset(buf1, 0, sizeof(buf1));
memset(buf2, 0, sizeof(buf2));
@@ -226,21 +203,47 @@
"v6 res_nsend 2nd check answers");
EXPECT_EQ(env, 0, expectAnswersValid(env, fd1, AF_INET6, ns_r_noerror),
"v6 res_nsend 1st check answers");
-
- // V6 NXDOMAIN
- memset(buf1, 0, sizeof(buf1));
- len1 = makeQuery(kNxDomainName, ns_t_aaaa, buf1, sizeof(buf1));
- EXPECT_GT(env, len1, 0, "v6 res_mkquery NXDOMAIN");
- fd1 = android_res_nsend(handle, buf1, len1, 0);
- EXPECT_GE(env, fd1, 0, "v6 res_nsend NXDOMAIN");
- EXPECT_EQ(env, 0, expectAnswersValid(env, fd1, AF_INET6, ns_r_nxdomain),
- "v6 res_nsend NXDOMAIN check answers");
-
- return 0;
}
extern "C"
-JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runResNcancelCheck(
+JNIEXPORT void Java_android_net_cts_MultinetworkApiTest_runResNnxDomainCheck(
+ JNIEnv* env, jclass, jlong nethandle) {
+ net_handle_t handle = (net_handle_t) nethandle;
+
+ // res_nquery V4 NXDOMAIN
+ int fd = android_res_nquery(handle, kNxDomainName, ns_c_in, ns_t_a, 0);
+ EXPECT_GE(env, fd, 0, "v4 res_nquery NXDOMAIN");
+ EXPECT_EQ(env, 0, expectAnswersValid(env, fd, AF_INET, ns_r_nxdomain),
+ "v4 res_nquery NXDOMAIN check answers");
+
+ // res_nquery V6 NXDOMAIN
+ fd = android_res_nquery(handle, kNxDomainName, ns_c_in, ns_t_aaaa, 0);
+ EXPECT_GE(env, fd, 0, "v6 res_nquery NXDOMAIN");
+ EXPECT_EQ(env, 0, expectAnswersValid(env, fd, AF_INET6, ns_r_nxdomain),
+ "v6 res_nquery NXDOMAIN check answers");
+
+ uint8_t buf[MAXPACKET] = {};
+ // res_nsend V4 NXDOMAIN
+ int len = makeQuery(kNxDomainName, ns_t_a, buf, sizeof(buf));
+ EXPECT_GT(env, len, 0, "v4 res_mkquery NXDOMAIN");
+ fd = android_res_nsend(handle, buf, len, 0);
+ EXPECT_GE(env, fd, 0, "v4 res_nsend NXDOMAIN");
+ EXPECT_EQ(env, 0, expectAnswersValid(env, fd, AF_INET, ns_r_nxdomain),
+ "v4 res_nsend NXDOMAIN check answers");
+
+ // res_nsend V6 NXDOMAIN
+ memset(buf, 0, sizeof(buf));
+ len = makeQuery(kNxDomainName, ns_t_aaaa, buf, sizeof(buf));
+ EXPECT_GT(env, len, 0, "v6 res_mkquery NXDOMAIN");
+ fd = android_res_nsend(handle, buf, len, 0);
+ EXPECT_GE(env, fd, 0, "v6 res_nsend NXDOMAIN");
+ EXPECT_EQ(env, 0, expectAnswersValid(env, fd, AF_INET6, ns_r_nxdomain),
+ "v6 res_nsend NXDOMAIN check answers");
+}
+
+
+extern "C"
+JNIEXPORT void Java_android_net_cts_MultinetworkApiTest_runResNcancelCheck(
JNIEnv* env, jclass, jlong nethandle) {
net_handle_t handle = (net_handle_t) nethandle;
@@ -251,11 +254,10 @@
EXPECT_EQ(env, 0, err, "res_cancel");
// DO NOT call cancel or result with the same fd more than once,
// otherwise it will hit fdsan double-close fd.
- return 0;
}
extern "C"
-JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runResNapiMalformedCheck(
+JNIEXPORT void Java_android_net_cts_MultinetworkApiTest_runResNapiMalformedCheck(
JNIEnv* env, jclass, jlong nethandle) {
net_handle_t handle = (net_handle_t) nethandle;
@@ -311,8 +313,6 @@
EXPECT_GE(env, fd, 0, "res_nsend 500 bytes filled with 0xFF");
EXPECT_EQ(env, 0, expectAnswersNotValid(env, fd, -EINVAL),
"res_nsend 500 bytes filled with 0xFF check answers");
-
- return 0;
}
extern "C"
diff --git a/tests/tests/net/src/android/net/cts/DnsResolverTest.java b/tests/tests/net/src/android/net/cts/DnsResolverTest.java
index ef8badd..c32a7a0 100644
--- a/tests/tests/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/tests/net/src/android/net/cts/DnsResolverTest.java
@@ -21,6 +21,7 @@
import static android.net.DnsResolver.FLAG_NO_CACHE_LOOKUP;
import static android.net.DnsResolver.TYPE_A;
import static android.net.DnsResolver.TYPE_AAAA;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.system.OsConstants.ETIMEDOUT;
import android.annotation.NonNull;
@@ -36,6 +37,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.ParseException;
+import android.net.cts.util.CtsNetUtils;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
@@ -62,7 +64,9 @@
};
static final String TEST_DOMAIN = "www.google.com";
+ static final String TEST_NX_DOMAIN = "test1-nx.metric.gstatic.com";
static final String INVALID_PRIVATE_DNS_SERVER = "invalid.google";
+ static final String GOOGLE_PRIVATE_DNS_SERVER = "dns.google";
static final byte[] TEST_BLOB = new byte[]{
/* Header */
0x55, 0x66, /* Transaction ID */
@@ -86,6 +90,7 @@
private ContentResolver mCR;
private ConnectivityManager mCM;
+ private CtsNetUtils mCtsNetUtils;
private Executor mExecutor;
private Executor mExecutorInline;
private DnsResolver mDns;
@@ -101,6 +106,7 @@
mExecutor = new Handler(Looper.getMainLooper())::post;
mExecutorInline = (Runnable r) -> r.run();
mCR = getContext().getContentResolver();
+ mCtsNetUtils = new CtsNetUtils(getContext());
storePrivateDnsSetting();
}
@@ -309,6 +315,14 @@
doTestRawQueryNXDomain(mExecutorInline);
}
+ public void testRawQueryNXDomainWithPrivateDns() throws Exception {
+ doTestRawQueryNXDomainWithPrivateDns(mExecutor);
+ }
+
+ public void testRawQueryNXDomainInlineWithPrivateDns() throws Exception {
+ doTestRawQueryNXDomainWithPrivateDns(mExecutorInline);
+ }
+
public void doTestRawQuery(Executor executor) throws InterruptedException {
final String msg = "RawQuery " + TEST_DOMAIN;
for (Network network : getTestableNetworks()) {
@@ -364,11 +378,46 @@
}
public void doTestRawQueryNXDomain(Executor executor) throws InterruptedException {
- final String dname = "test1-nx.metric.gstatic.com";
- final String msg = "RawQuery " + dname;
+ final String msg = "RawQuery " + TEST_NX_DOMAIN;
+
for (Network network : getTestableNetworks()) {
+ final NetworkCapabilities nc = (network != null)
+ ? mCM.getNetworkCapabilities(network)
+ : mCM.getNetworkCapabilities(mCM.getActiveNetwork());
+ assertNotNull("Couldn't determine NetworkCapabilities for " + network, nc);
+ // Some cellular networks configure their DNS servers never to return NXDOMAIN, so don't
+ // test NXDOMAIN on these DNS servers.
+ // b/144521720
+ if (nc.hasTransport(TRANSPORT_CELLULAR)) continue;
final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
- mDns.rawQuery(network, dname, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
+ mDns.rawQuery(network, TEST_NX_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
+ executor, null, callback);
+
+ assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
+ callback.waitForAnswer());
+ callback.assertNXDomain();
+ }
+ }
+
+ public void doTestRawQueryNXDomainWithPrivateDns(Executor executor)
+ throws InterruptedException {
+ final String msg = "RawQuery " + TEST_NX_DOMAIN + " with private DNS";
+
+ // Enable private DNS strict mode and set server to dns.google before doing NxDomain test.
+ // b/144521720
+ Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
+ Settings.Global.putString(mCR,
+ Settings.Global.PRIVATE_DNS_SPECIFIER, GOOGLE_PRIVATE_DNS_SERVER);
+
+ for (Network network : getTestableNetworks()) {
+ final Network networkForPrivateDns =
+ (network != null) ? network : mCM.getActiveNetwork();
+ assertNotNull("Can't find network to await private DNS on", networkForPrivateDns);
+ mCtsNetUtils.awaitPrivateDnsSetting(msg + " wait private DNS setting timeout",
+ networkForPrivateDns, GOOGLE_PRIVATE_DNS_SERVER,
+ PRIVATE_DNS_SETTING_TIMEOUT_MS);
+ final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
+ mDns.rawQuery(network, TEST_NX_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
executor, null, callback);
assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
@@ -615,23 +664,6 @@
}
}
- private void awaitPrivateDnsSetting(@NonNull String msg,
- @NonNull Network network, @NonNull String server) throws InterruptedException {
- CountDownLatch latch = new CountDownLatch(1);
- NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
- NetworkCallback callback = new NetworkCallback() {
- @Override
- public void onLinkPropertiesChanged(Network n, LinkProperties lp) {
- if (network.equals(n) && server.equals(lp.getPrivateDnsServerName())) {
- latch.countDown();
- }
- }
- };
- mCM.registerNetworkCallback(request, callback);
- assertTrue(msg, latch.await(PRIVATE_DNS_SETTING_TIMEOUT_MS, TimeUnit.MILLISECONDS));
- mCM.unregisterNetworkCallback(callback);
- }
-
public void testPrivateDnsBypass() throws InterruptedException {
final Network[] testNetworks = getTestableNetworks();
@@ -647,8 +679,8 @@
if (network == null) continue;
// wait for private DNS setting propagating
- awaitPrivateDnsSetting(msg + " wait private DNS setting timeout",
- network, INVALID_PRIVATE_DNS_SERVER);
+ mCtsNetUtils.awaitPrivateDnsSetting(msg + " wait private DNS setting timeout",
+ network, INVALID_PRIVATE_DNS_SERVER, PRIVATE_DNS_SETTING_TIMEOUT_MS);
final CountDownLatch latch = new CountDownLatch(1);
final DnsResolver.Callback<List<InetAddress>> errorCallback =
diff --git a/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java b/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
index c3e65b7..88e86f4 100644
--- a/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
+++ b/tests/tests/net/src/android/net/cts/MultinetworkApiTest.java
@@ -16,18 +16,22 @@
package android.net.cts;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
import android.content.Context;
+import android.content.ContentResolver;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkUtils;
+import android.net.cts.util.CtsNetUtils;
+import android.provider.Settings;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.test.AndroidTestCase;
import java.util.ArrayList;
-
public class MultinetworkApiTest extends AndroidTestCase {
static {
@@ -35,6 +39,8 @@
}
private static final String TAG = "MultinetworkNativeApiTest";
+ static final String GOOGLE_PRIVATE_DNS_SERVER = "dns.google";
+ static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 2_000;
/**
* @return 0 on success
@@ -43,18 +49,44 @@
private static native int runSetprocnetwork(long networkHandle);
private static native int runSetsocknetwork(long networkHandle);
private static native int runDatagramCheck(long networkHandle);
- private static native int runResNapiMalformedCheck(long networkHandle);
- private static native int runResNcancelCheck(long networkHandle);
- private static native int runResNqueryCheck(long networkHandle);
- private static native int runResNsendCheck(long networkHandle);
+ private static native void runResNapiMalformedCheck(long networkHandle);
+ private static native void runResNcancelCheck(long networkHandle);
+ private static native void runResNqueryCheck(long networkHandle);
+ private static native void runResNsendCheck(long networkHandle);
+ private static native void runResNnxDomainCheck(long networkHandle);
-
+ private ContentResolver mCR;
private ConnectivityManager mCM;
+ private CtsNetUtils mCtsNetUtils;
+ private String mOldMode;
+ private String mOldDnsSpecifier;
+ @Override
protected void setUp() throws Exception {
super.setUp();
mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ mCR = getContext().getContentResolver();
+ mCtsNetUtils = new CtsNetUtils(getContext());
+ storePrivateDnsSetting();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ restorePrivateDnsSetting();
+ super.tearDown();
+ }
+
+ private void storePrivateDnsSetting() {
+ // Store private DNS setting
+ mOldMode = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_MODE);
+ mOldDnsSpecifier = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER);
+ }
+
+ private void restorePrivateDnsSetting() {
+ // restore private DNS setting
+ Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, mOldMode);
+ Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER, mOldDnsSpecifier);
}
private Network[] getTestableNetworks() {
@@ -182,13 +214,34 @@
} catch (IllegalArgumentException e) {}
}
- public void testResNApi() {
- for (Network network : getTestableNetworks()) {
+ public void testResNApi() throws InterruptedException {
+ final Network[] testNetworks = getTestableNetworks();
+
+ for (Network network : testNetworks) {
// Throws AssertionError directly in jni function if test fail.
runResNqueryCheck(network.getNetworkHandle());
runResNsendCheck(network.getNetworkHandle());
runResNcancelCheck(network.getNetworkHandle());
runResNapiMalformedCheck(network.getNetworkHandle());
+
+ final NetworkCapabilities nc = mCM.getNetworkCapabilities(network);
+ // Some cellular networks configure their DNS servers never to return NXDOMAIN, so don't
+ // test NXDOMAIN on these DNS servers.
+ // b/144521720
+ if (nc != null && !nc.hasTransport(TRANSPORT_CELLULAR)) {
+ runResNnxDomainCheck(network.getNetworkHandle());
+ }
+ }
+ // Enable private DNS strict mode and set server to dns.google before doing NxDomain test.
+ // b/144521720
+ Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
+ Settings.Global.putString(mCR,
+ Settings.Global.PRIVATE_DNS_SPECIFIER, GOOGLE_PRIVATE_DNS_SERVER);
+ for (Network network : testNetworks) {
+ // Wait for private DNS setting to propagate.
+ mCtsNetUtils.awaitPrivateDnsSetting("NxDomain test wait private DNS setting timeout",
+ network, GOOGLE_PRIVATE_DNS_SERVER, PRIVATE_DNS_SETTING_TIMEOUT_MS);
+ runResNnxDomainCheck(network.getNetworkHandle());
}
}
}
diff --git a/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
index e19d2ba..f0c34e3 100644
--- a/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/tests/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -24,12 +24,14 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
@@ -243,6 +245,23 @@
return s;
}
+ public void awaitPrivateDnsSetting(@NonNull String msg, @NonNull Network network,
+ @NonNull String server, int timeoutMs) throws InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+ NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
+ NetworkCallback callback = new NetworkCallback() {
+ @Override
+ public void onLinkPropertiesChanged(Network n, LinkProperties lp) {
+ if (network.equals(n) && server.equals(lp.getPrivateDnsServerName())) {
+ latch.countDown();
+ }
+ }
+ };
+ mCm.registerNetworkCallback(request, callback);
+ assertTrue(msg, latch.await(timeoutMs, TimeUnit.MILLISECONDS));
+ mCm.unregisterNetworkCallback(callback);
+ }
+
/**
* Receiver that captures the last connectivity change's network type and state. Recognizes
* both {@code CONNECTIVITY_ACTION} and {@code NETWORK_CALLBACK_ACTION} intents.
diff --git a/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java b/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
index 4670928..c15b7a4 100755
--- a/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
+++ b/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.ACCESS_MEDIA_LOCATION;
import static android.Manifest.permission.READ_CALL_LOG;
import static android.Manifest.permission.READ_CONTACTS;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
@@ -104,6 +105,13 @@
case ACCESS_COARSE_LOCATION:
assertSplit(split, ACCESS_BACKGROUND_LOCATION, Build.VERSION_CODES.Q);
break;
+ case READ_EXTERNAL_STORAGE:
+ assertSplit(split, ACCESS_MEDIA_LOCATION, Build.VERSION_CODES.Q);
+ // Remove this split permission from seenSplits, ACCESS_MEDIA_LOCATION is not
+ // always available hence removing this permission from seenSplits will
+ // avoid seenSplits size check fail.
+ seenSplits.remove(split);
+ break;
}
}
diff --git a/tests/tests/permission/src/android/permission/cts/UndefinedGroupPermissionTest.java b/tests/tests/permission/src/android/permission/cts/UndefinedGroupPermissionTest.java
index b4a5dce..1dc068d 100644
--- a/tests/tests/permission/src/android/permission/cts/UndefinedGroupPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/UndefinedGroupPermissionTest.java
@@ -149,7 +149,7 @@
mUiDevice.findObject(new UiSelector().resourceId(
"com.android.permissioncontroller:id/permission_allow_button"))
.getClassName();
- } catch (UiObjectNotFoundException e) {
+ } catch (UiObjectNotFoundException tolerated) {
assertEquals("grant dialog never showed.",
mPm.checkPermission(targetPermission,
APP_PKG_NAME), PERMISSION_GRANTED);
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index e1bb976..f93376d 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -1207,6 +1207,15 @@
android:protectionLevel="dangerous|instant" />
+ <!-- Allows receiving the camera service notifications when a camera is opened
+ (by a certain application package) or closed.
+ @hide -->
+ <permission android:name="android.permission.CAMERA_OPEN_CLOSE_LISTENER"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_cameraOpenCloseListener"
+ android:description="@string/permdesc_cameraOpenCloseListener"
+ android:protectionLevel="signature" />
+
<!-- ====================================================================== -->
<!-- Permissions for accessing the device sensors -->
<!-- ====================================================================== -->
@@ -4403,12 +4412,12 @@
<!-- @SystemApi Allows to access all app shortcuts.
@hide -->
<permission android:name="android.permission.ACCESS_SHORTCUTS"
- android:protectionLevel="signature|textClassifier" />
+ android:protectionLevel="signature|appPredictor" />
<!-- @SystemApi Allows unlimited calls to shortcut mutation APIs.
@hide -->
<permission android:name="android.permission.UNLIMITED_SHORTCUTS_API_CALLS"
- android:protectionLevel="signature|textClassifier" />
+ android:protectionLevel="signature|appPredictor" />
<!-- @SystemApi Allows an application to read the runtime profiles of other apps.
@hide <p>Not for use by third-party applications. -->
diff --git a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
index cdeb775..2cdc7cb 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
@@ -28,6 +28,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
+import android.os.Build;
import android.os.storage.StorageManager;
import android.platform.test.annotations.AppModeFull;
import android.util.ArrayMap;
@@ -86,6 +87,9 @@
private static final String ATTR_PROTECTION_LEVEL = "protectionLevel";
private static final String ATTR_BACKGROUND_PERMISSION = "backgroundPermission";
+ private static final String CAMERA_OPEN_CLOSE_LISTENER_PERMISSION =
+ "android.permission.CAMERA_OPEN_CLOSE_LISTENER";
+
private static final Context sContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
@@ -125,6 +129,13 @@
continue;
}
+ // Skip CAMERA OPEN_CLOSE_LISTENER_PERMISSION check for Android Q
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q
+ && expectedPermissionName.equals(CAMERA_OPEN_CLOSE_LISTENER_PERMISSION)) {
+ declaredPermissionsMap.remove(expectedPermissionName);
+ continue;
+ }
+
// OEMs cannot remove permissions
PermissionInfo declaredPermission = declaredPermissionsMap.get(expectedPermissionName);
if (declaredPermission == null) {
diff --git a/tests/tests/security/res/raw/bug_123700383.mid b/tests/tests/security/res/raw/bug_123700383.mid
new file mode 100644
index 0000000..1e1ae6b
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_123700383.mid
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_127310810.mid b/tests/tests/security/res/raw/bug_127310810.mid
new file mode 100644
index 0000000..8a64142
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_127310810.mid
@@ -0,0 +1,3 @@
+BEGIN:IMELODY
+BEAT:900
+MELODY:((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((ledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonledonr5;@32767)
\ No newline at end of file
diff --git a/tests/tests/security/res/raw/bug_127312550.mid b/tests/tests/security/res/raw/bug_127312550.mid
new file mode 100644
index 0000000..ea66e75
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_127312550.mid
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_127313223.mid b/tests/tests/security/res/raw/bug_127313223.mid
new file mode 100644
index 0000000..6558be7
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_127313223.mid
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_127313537.mid b/tests/tests/security/res/raw/bug_127313537.mid
new file mode 100644
index 0000000..658ab92
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_127313537.mid
Binary files differ
diff --git a/tests/tests/security/res/raw/bug_127313764.mid b/tests/tests/security/res/raw/bug_127313764.mid
new file mode 100644
index 0000000..bda2dfb
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_127313764.mid
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index 63c958c..611d0b6 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -2424,6 +2424,42 @@
new CrashUtils.Config().checkMinAddress(false));
}
+ @Test
+ @SecurityTest(minPatchLevel = "Unknown")
+ public void testBug_123700383() throws Exception {
+ assertExtractorDoesNotHang(R.raw.bug_123700383);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "Unknown")
+ public void testBug_127310810() throws Exception {
+ assertExtractorDoesNotHang(R.raw.bug_127310810);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "Unknown")
+ public void testBug_127312550() throws Exception {
+ assertExtractorDoesNotHang(R.raw.bug_127312550);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "Unknown")
+ public void testBug_127313223() throws Exception {
+ assertExtractorDoesNotHang(R.raw.bug_127313223);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "Unknown")
+ public void testBug_127313537() throws Exception {
+ assertExtractorDoesNotHang(R.raw.bug_127313537);
+ }
+
+ @Test
+ @SecurityTest(minPatchLevel = "Unknown")
+ public void testBug_127313764() throws Exception {
+ assertExtractorDoesNotHang(R.raw.bug_127313764);
+ }
+
private int[] getFrameSizes(int rid) throws IOException {
final Context context = getInstrumentation().getContext();
final Resources resources = context.getResources();
@@ -2994,6 +3030,37 @@
thr.join();
}
+ protected void assertExtractorDoesNotHang(int rid) throws Exception {
+ // The media extractor has a watchdog, currently set to 10 seconds.
+ final long timeoutMs = 12 * 1000;
+
+ Thread thread = new Thread(() -> {
+ MediaExtractor ex = new MediaExtractor();
+ AssetFileDescriptor fd =
+ getInstrumentation().getContext().getResources().openRawResourceFd(rid);
+ try {
+ ex.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
+ } catch (IOException e) {
+ // It is OK for the call to fail, we're only making sure it doesn't hang.
+ } finally {
+ closeQuietly(fd);
+ ex.release();
+ }
+ });
+ thread.start();
+
+ thread.join(timeoutMs);
+ boolean hung = thread.isAlive();
+ if (hung) {
+ // We don't have much to do at this point. Attempt to un-hang the thread, the media
+ // extractor process is likely still spinning. At least we found a bug...
+ // TODO: reboot the media extractor process.
+ thread.interrupt();
+ }
+
+ assertFalse(hung);
+ }
+
private Instrumentation getInstrumentation() {
return mInstrumentation;
}
diff --git a/tests/tests/view/src/android/view/cts/SystemGestureExclusionRectsTest.java b/tests/tests/view/src/android/view/cts/SystemGestureExclusionRectsTest.java
index 423f3b7..25312e7 100644
--- a/tests/tests/view/src/android/view/cts/SystemGestureExclusionRectsTest.java
+++ b/tests/tests/view/src/android/view/cts/SystemGestureExclusionRectsTest.java
@@ -201,9 +201,62 @@
assertTrue("set rects timeout", setter[0].await(3, SECONDS));
}
+ @Test
+ public void ignoreHiddenViewRects() throws Throwable {
+ final Activity activity = mActivityRule.getActivity();
+ final View contentView = activity.findViewById(R.id.abslistview_root);
+ final List<Rect> dummyLocalExclusionRects = Lists.newArrayList(new Rect(0, 0, 5, 5));
+ final List<Rect> dummyWindowExclusionRects = new ArrayList<>();
+
+ mActivityRule.runOnUiThread(() -> {
+ final View v = activity.findViewById(R.id.animating_view);
+ int[] point = new int[2];
+ v.getLocationInWindow(point);
+ for (Rect r : dummyLocalExclusionRects) {
+ Rect offsetR = new Rect(r);
+ offsetR.offsetTo(point[0], point[1]);
+ dummyWindowExclusionRects.add(offsetR);
+ }
+ });
+
+ // Set an exclusion rect on the animating view, ensure it's reported
+ final GestureExclusionLatcher[] setLatch = new GestureExclusionLatcher[1];
+ mActivityRule.runOnUiThread(() -> {
+ final View v = activity.findViewById(R.id.animating_view);
+ setLatch[0] = GestureExclusionLatcher.watching(v.getViewTreeObserver());
+ v.setSystemGestureExclusionRects(dummyLocalExclusionRects);
+ });
+ assertTrue("set rects timeout", setLatch[0].await(3, SECONDS));
+ assertEquals("returned rects as expected", dummyWindowExclusionRects,
+ setLatch[0].getLastReportedRects());
+
+ // Hide the content view, ensure that the reported rects are null for the child view
+ final GestureExclusionLatcher[] updateHideLatch = new GestureExclusionLatcher[1];
+ mActivityRule.runOnUiThread(() -> {
+ final View v = activity.findViewById(R.id.animating_view);
+ updateHideLatch[0] = GestureExclusionLatcher.watching(v.getViewTreeObserver());
+ contentView.setVisibility(View.INVISIBLE);
+ });
+ assertTrue("set rects timeout", updateHideLatch[0].await(3, SECONDS));
+ assertEquals("returned rects as expected", Collections.EMPTY_LIST,
+ updateHideLatch[0].getLastReportedRects());
+
+ // Show the content view again, ensure that the reported rects are valid for the child view
+ final GestureExclusionLatcher[] updateShowLatch = new GestureExclusionLatcher[1];
+ mActivityRule.runOnUiThread(() -> {
+ final View v = activity.findViewById(R.id.animating_view);
+ updateShowLatch[0] = GestureExclusionLatcher.watching(v.getViewTreeObserver());
+ contentView.setVisibility(View.VISIBLE);
+ });
+ assertTrue("set rects timeout", updateShowLatch[0].await(3, SECONDS));
+ assertEquals("returned rects as expected", dummyWindowExclusionRects,
+ updateShowLatch[0].getLastReportedRects());
+ }
+
private static class GestureExclusionLatcher implements Consumer<List<Rect>> {
private final CountDownLatch mLatch = new CountDownLatch(1);
private final ViewTreeObserver mVto;
+ private List<Rect> mLastReportedRects = Collections.EMPTY_LIST;
public static GestureExclusionLatcher watching(ViewTreeObserver vto) {
final GestureExclusionLatcher latcher = new GestureExclusionLatcher(vto);
@@ -219,8 +272,13 @@
return mLatch.await(time, unit);
}
+ public List<Rect> getLastReportedRects() {
+ return mLastReportedRects;
+ }
+
@Override
public void accept(List<Rect> rects) {
+ mLastReportedRects = rects;
mLatch.countDown();
mVto.removeOnSystemGestureExclusionRectsChangedListener(this);
}