Merge "Import translations. DO NOT MERGE" into nyc-dev
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 6424520..04883a9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5021,6 +5021,9 @@
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
if (!Process.isIsolated() && !"android".equals(appContext.getPackageName())) {
+ // This cache location probably points at credential-encrypted
+ // storage which may not be accessible yet; assign it anyway instead
+ // of pointing at device-encrypted storage.
final File cacheDir = appContext.getCacheDir();
if (cacheDir != null) {
// Provide a usable directory for temporary files
@@ -5030,8 +5033,12 @@
+ "due to missing cache directory");
}
- // Use codeCacheDir to store generated/compiled graphics code and jit profiling data.
- final File codeCacheDir = appContext.getCodeCacheDir();
+ // Setup a location to store generated/compiled graphics code and
+ // JIT profiling data. Note that this data is stored in a
+ // device-encrypted storage area, so these caches must never contain
+ // user sensitive user data.
+ final Context deviceContext = appContext.createDeviceEncryptedStorageContext();
+ final File codeCacheDir = deviceContext.getCodeCacheDir();
if (codeCacheDir != null) {
setupGraphicsSupport(data.info, codeCacheDir);
setupJitProfileSupport(data.info, codeCacheDir);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 89d4931..eec503b 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -58,6 +58,9 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.storage.IMountService;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
import android.util.Log;
@@ -482,21 +485,20 @@
return f.delete();
}
- // Common-path handling of app data dir creation
+ /**
+ * Common-path handling of app data dir creation
+ */
private static File ensurePrivateDirExists(File file) {
if (!file.exists()) {
- if (!file.mkdirs()) {
- if (file.exists()) {
- // spurious failure; probably racing with another process for this app
- return file;
+ try {
+ Os.mkdir(file.getAbsolutePath(), 0771);
+ } catch (ErrnoException e) {
+ if (e.errno == OsConstants.EEXIST) {
+ // We must have raced with someone; that's okay
+ } else {
+ Log.w(TAG, "Failed to ensure " + file + ": " + e.getMessage());
}
- Log.w(TAG, "Failed to ensure directory " + file.getAbsolutePath());
- return null;
}
- FileUtils.setPermissions(
- file.getPath(),
- FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
- -1, -1);
}
return file;
}
diff --git a/core/java/android/transition/Fade.java b/core/java/android/transition/Fade.java
index 287c696..b2e8d33 100644
--- a/core/java/android/transition/Fade.java
+++ b/core/java/android/transition/Fade.java
@@ -57,9 +57,9 @@
* tag <code>fade</code>, along with the standard
* attributes of {@link android.R.styleable#Fade} and
* {@link android.R.styleable#Transition}.</p>
-
*/
public class Fade extends Visibility {
+ static final String PROPNAME_TRANSITION_ALPHA = "android:fade:transitionAlpha";
private static boolean DBG = Transition.DBG && false;
@@ -105,6 +105,13 @@
setMode(fadingMode);
}
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ transitionValues.values.put(PROPNAME_TRANSITION_ALPHA,
+ transitionValues.view.getTransitionAlpha());
+ }
+
/**
* Utility method to handle creating and running the Animator.
*/
@@ -119,7 +126,6 @@
}
final FadeAnimatorListener listener = new FadeAnimatorListener(view);
anim.addListener(listener);
- anim.addPauseListener(listener);
addListener(new TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
@@ -138,18 +144,28 @@
Log.d(LOG_TAG, "Fade.onAppear: startView, startVis, endView, endVis = " +
startView + ", " + view);
}
- return createAnimation(view, 0, 1);
+ float startAlpha = 0;
+ if (startValues != null) {
+ startAlpha = (Float) startValues.values.get(PROPNAME_TRANSITION_ALPHA);
+ if (startAlpha == 1) {
+ startAlpha = 0;
+ }
+ }
+ return createAnimation(view, startAlpha, 1);
}
@Override
public Animator onDisappear(ViewGroup sceneRoot, final View view, TransitionValues startValues,
TransitionValues endValues) {
- return createAnimation(view, 1, 0);
+ float startAlpha = 1;
+ if (startValues != null) {
+ startAlpha = (Float) startValues.values.get(PROPNAME_TRANSITION_ALPHA);
+ }
+ return createAnimation(view, startAlpha, 0);
}
private static class FadeAnimatorListener extends AnimatorListenerAdapter {
private final View mView;
- private float mPausedAlpha = -1;
private boolean mLayerTypeChanged = false;
public FadeAnimatorListener(View view) {
@@ -171,16 +187,5 @@
mView.setLayerType(View.LAYER_TYPE_NONE, null);
}
}
-
- @Override
- public void onAnimationPause(Animator animator) {
- mPausedAlpha = mView.getTransitionAlpha();
- mView.setTransitionAlpha(1);
- }
-
- @Override
- public void onAnimationResume(Animator animator) {
- mView.setTransitionAlpha(mPausedAlpha);
- }
}
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 1740f07..5b9930b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -166,6 +166,7 @@
in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
int icon, int logo, int windowFlags, IBinder transferFrom, boolean createIfNeeded);
void setAppVisibility(IBinder token, boolean visible);
+ void notifyAppStopped(IBinder token);
void startAppFreezingScreen(IBinder token, int configChanges);
void stopAppFreezingScreen(IBinder token, boolean force);
void removeAppToken(IBinder token);
diff --git a/core/tests/coretests/res/layout/animator_set_squares.xml b/core/tests/coretests/res/layout/animator_set_squares.xml
index 23e6eea..6888248f 100644
--- a/core/tests/coretests/res/layout/animator_set_squares.xml
+++ b/core/tests/coretests/res/layout/animator_set_squares.xml
@@ -4,7 +4,8 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/white"
- android:orientation="horizontal">
+ android:orientation="horizontal"
+ android:id="@+id/container">
<View
android:layout_width="50dp"
android:layout_height="50dp"
diff --git a/core/tests/coretests/src/android/transition/FadeTransitionTest.java b/core/tests/coretests/src/android/transition/FadeTransitionTest.java
new file mode 100644
index 0000000..dc60423
--- /dev/null
+++ b/core/tests/coretests/src/android/transition/FadeTransitionTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2016 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.transition;
+
+import android.animation.AnimatorSetActivity;
+import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.transition.Transition.TransitionListener;
+import android.transition.Transition.TransitionListenerAdapter;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.frameworks.coretests.R;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static android.support.test.espresso.Espresso.onView;
+
+public class FadeTransitionTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
+ Activity mActivity;
+ public FadeTransitionTest() {
+ super(AnimatorSetActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ mActivity = getActivity();
+ }
+
+ @SmallTest
+ public void testFadeOutAndIn() throws Throwable {
+ View square1 = mActivity.findViewById(R.id.square1);
+ Fade fadeOut = new Fade(Fade.MODE_OUT);
+ TransitionLatch latch = setVisibilityInTransition(fadeOut, R.id.square1, View.INVISIBLE);
+ assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS));
+ assertEquals(View.VISIBLE, square1.getVisibility());
+ Thread.sleep(100);
+ assertFalse(square1.getTransitionAlpha() == 0 || square1.getTransitionAlpha() == 1);
+ assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS));
+ assertEquals(1.0f, square1.getTransitionAlpha());
+ assertEquals(View.INVISIBLE, square1.getVisibility());
+
+ Fade fadeIn = new Fade(Fade.MODE_IN);
+ latch = setVisibilityInTransition(fadeIn, R.id.square1, View.VISIBLE);
+ assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS));
+ assertEquals(View.VISIBLE, square1.getVisibility());
+ Thread.sleep(100);
+ final float transitionAlpha = square1.getTransitionAlpha();
+ assertTrue("expecting transitionAlpha to be between 0 and 1. Was " + transitionAlpha,
+ transitionAlpha > 0 && transitionAlpha < 1);
+ assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS));
+ assertEquals(1.0f, square1.getTransitionAlpha());
+ assertEquals(View.VISIBLE, square1.getVisibility());
+ }
+
+ @SmallTest
+ public void testFadeOutInterrupt() throws Throwable {
+ View square1 = mActivity.findViewById(R.id.square1);
+ Fade fadeOut = new Fade(Fade.MODE_OUT);
+ FadeValueCheck fadeOutValueCheck = new FadeValueCheck(square1);
+ fadeOut.addListener(fadeOutValueCheck);
+ TransitionLatch outLatch = setVisibilityInTransition(fadeOut, R.id.square1, View.INVISIBLE);
+ assertTrue(outLatch.startLatch.await(200, TimeUnit.MILLISECONDS));
+ Thread.sleep(100);
+
+ Fade fadeIn = new Fade(Fade.MODE_IN);
+ FadeValueCheck fadeInValueCheck = new FadeValueCheck(square1);
+ fadeIn.addListener(fadeInValueCheck);
+ TransitionLatch inLatch = setVisibilityInTransition(fadeIn, R.id.square1, View.VISIBLE);
+ assertTrue(inLatch.startLatch.await(200, TimeUnit.MILLISECONDS));
+
+ assertEquals(fadeOutValueCheck.pauseTransitionAlpha, fadeInValueCheck.startTransitionAlpha);
+ assertTrue("expecting transitionAlpha to be between 0 and 1. Was " +
+ fadeOutValueCheck.pauseTransitionAlpha,
+ fadeOutValueCheck.pauseTransitionAlpha > 0 &&
+ fadeOutValueCheck.pauseTransitionAlpha < 1);
+
+ assertTrue(inLatch.endLatch.await(400, TimeUnit.MILLISECONDS));
+ assertEquals(1.0f, square1.getTransitionAlpha());
+ assertEquals(View.VISIBLE, square1.getVisibility());
+ }
+
+ @SmallTest
+ public void testFadeInInterrupt() throws Throwable {
+ final View square1 = mActivity.findViewById(R.id.square1);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ square1.setVisibility(View.INVISIBLE);
+ }
+ });
+ Fade fadeIn = new Fade(Fade.MODE_IN);
+ FadeValueCheck fadeInValueCheck = new FadeValueCheck(square1);
+ fadeIn.addListener(fadeInValueCheck);
+ TransitionLatch inLatch = setVisibilityInTransition(fadeIn, R.id.square1, View.VISIBLE);
+ assertTrue(inLatch.startLatch.await(200, TimeUnit.MILLISECONDS));
+ Thread.sleep(100);
+
+ Fade fadeOut = new Fade(Fade.MODE_OUT);
+ FadeValueCheck fadeOutValueCheck = new FadeValueCheck(square1);
+ fadeOut.addListener(fadeOutValueCheck);
+ TransitionLatch outLatch = setVisibilityInTransition(fadeOut, R.id.square1, View.INVISIBLE);
+ assertTrue(outLatch.startLatch.await(200, TimeUnit.MILLISECONDS));
+
+ assertEquals(fadeOutValueCheck.pauseTransitionAlpha, fadeInValueCheck.startTransitionAlpha);
+ assertTrue("expecting transitionAlpha to be between 0 and 1. Was " +
+ fadeInValueCheck.pauseTransitionAlpha,
+ fadeInValueCheck.pauseTransitionAlpha > 0 &&
+ fadeInValueCheck.pauseTransitionAlpha < 1);
+
+ assertTrue(outLatch.endLatch.await(400, TimeUnit.MILLISECONDS));
+ assertEquals(1.0f, square1.getTransitionAlpha());
+ assertEquals(View.INVISIBLE, square1.getVisibility());
+ }
+
+ public TransitionLatch setVisibilityInTransition(final Transition transition, int viewId,
+ final int visibility) throws Throwable {
+ final ViewGroup sceneRoot = (ViewGroup) mActivity.findViewById(R.id.container);
+ final View view = sceneRoot.findViewById(viewId);
+ TransitionLatch latch = new TransitionLatch();
+ transition.addListener(latch);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.beginDelayedTransition(sceneRoot, transition);
+ view.setVisibility(visibility);
+ }
+ });
+ return latch;
+ }
+
+ public static class TransitionLatch implements TransitionListener {
+ public CountDownLatch startLatch = new CountDownLatch(1);
+ public CountDownLatch endLatch = new CountDownLatch(1);
+ public CountDownLatch cancelLatch = new CountDownLatch(1);
+ public CountDownLatch pauseLatch = new CountDownLatch(1);
+ public CountDownLatch resumeLatch = new CountDownLatch(1);
+
+ @Override
+ public void onTransitionStart(Transition transition) {
+ startLatch.countDown();
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ endLatch.countDown();
+ transition.removeListener(this);
+ }
+
+ @Override
+ public void onTransitionCancel(Transition transition) {
+ cancelLatch.countDown();
+ }
+
+ @Override
+ public void onTransitionPause(Transition transition) {
+ pauseLatch.countDown();
+ }
+
+ @Override
+ public void onTransitionResume(Transition transition) {
+ resumeLatch.countDown();
+ }
+ }
+
+ private static class FadeValueCheck extends TransitionListenerAdapter {
+ public float startTransitionAlpha;
+ public float pauseTransitionAlpha;
+ private final View mView;
+
+ public FadeValueCheck(View view) {
+ mView = view;
+ }
+ @Override
+ public void onTransitionStart(Transition transition) {
+ startTransitionAlpha = mView.getTransitionAlpha();
+ }
+
+ @Override
+ public void onTransitionPause(Transition transition) {
+ pauseTransitionAlpha = mView.getTransitionAlpha();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index c352fc8..9442da0 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1097,6 +1097,9 @@
mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
r.stopped = true;
r.state = ActivityState.STOPPED;
+
+ mWindowManager.notifyAppStopped(r.appToken);
+
if (getVisibleBehindActivity() == r) {
mStackSupervisor.requestVisibleBehindLocked(r, false);
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index fcb777b..8fa5d24 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2359,7 +2359,7 @@
}
final ApplicationInfo ai = pkg.pkg.applicationInfo;
- final String dataPath = new File(ai.dataDir).getCanonicalPath();
+ final String dataPath = ai.dataDir;
final boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
final int[] gids = pkg.getPermissionsState().computeGids(userIds);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 7ec945d..5a502c1 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -127,6 +127,8 @@
boolean mAlwaysFocusable;
+ boolean mAppStopped;
+
ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
AppWindowToken(WindowManagerService _service, IApplicationToken _token,
@@ -311,6 +313,47 @@
}
}
+ // Here we destroy surfaces which have been marked as eligible by the animator, taking care
+ // to ensure the client has finished with them. If the client could still be using them
+ // we will skip destruction and try again when the client has stopped.
+ void destroySurfaces() {
+ final ArrayList<WindowState> allWindows = (ArrayList<WindowState>) allAppWindows.clone();
+ final DisplayContentList displayList = new DisplayContentList();
+ for (int i = allWindows.size() - 1; i >= 0; i--) {
+ final WindowState win = allWindows.get(i);
+ if (!win.mDestroying) {
+ continue;
+ }
+
+ if (!mAppStopped && !win.mClientRemoveRequested) {
+ return;
+ }
+
+ win.destroyOrSaveSurface();
+ if (win.mRemoveOnExit) {
+ win.mExiting = false;
+ service.removeWindowInnerLocked(win);
+ }
+ final DisplayContent displayContent = win.getDisplayContent();
+ if (displayContent != null && !displayList.contains(displayContent)) {
+ displayList.add(displayContent);
+ }
+ win.mDestroying = false;
+ }
+ for (int i = 0; i < displayList.size(); i++) {
+ final DisplayContent displayContent = displayList.get(i);
+ service.mLayersController.assignLayersLocked(displayContent.getWindowList());
+ displayContent.layoutNeeded = true;
+ }
+ }
+
+ // The application has stopped, so destroy any surfaces which were keeping alive
+ // in case they were still being used.
+ void notifyAppStopped() {
+ mAppStopped = true;
+ destroySurfaces();
+ }
+
/**
* Checks whether we should save surfaces for this app.
*
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ae6c89a..3db16c7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2154,6 +2154,14 @@
if (win == null) {
return;
}
+ // We set this here instead of removeWindowLocked because we only want it to be
+ // true when the client has requested we remove the window. In other remove
+ // cases, we have to wait for activity stop to safely remove the window (as the
+ // client may still be using the surface). In this case though, the client has
+ // just dismissed a window (for example a Dialog) and activity stop isn't
+ // necessarily imminent, so we need to know not to wait for it after our
+ // hanimation (if applicable) finishes.
+ win.mClientRemoveRequested = true;
removeWindowLocked(win);
}
}
@@ -4188,6 +4196,24 @@
}
@Override
+ public void notifyAppStopped(IBinder token) {
+ if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
+ "notifyAppStopped()")) {
+ throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
+ }
+
+ synchronized(mWindowMap) {
+ final AppWindowToken wtoken;
+ wtoken = findAppWindowToken(token);
+ if (wtoken == null) {
+ Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token);
+ return;
+ }
+ wtoken.notifyAppStopped();
+ }
+ }
+
+ @Override
public void setAppVisibility(IBinder token, boolean visible) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setAppVisibility()")) {
@@ -4210,6 +4236,7 @@
mOpeningApps.remove(wtoken);
mClosingApps.remove(wtoken);
+ wtoken.mAppStopped = false;
wtoken.waitingToShow = false;
wtoken.hiddenRequested = !visible;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 880514c..114c212 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -386,6 +386,13 @@
boolean mRemoved;
/**
+ * Has the client requested we remove the window? In this case we know
+ * that we can dispose of it when we wish without further synchronization
+ * with the client
+ */
+ boolean mClientRemoveRequested;
+
+ /**
* Temp for keeping track of windows that have been removed when
* rebuilding window list.
*/
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c7c9cbf..0201296 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -471,16 +471,31 @@
if (WindowManagerService.localLOGV) Slog.v(
TAG, "Exit animation finished in " + this
+ ": remove=" + mWin.mRemoveOnExit);
- if (hasSurface()) {
- mService.mDestroySurface.add(mWin);
- mWin.mDestroying = true;
- hide("finishExit");
+
+
+ mWin.mDestroying = true;
+
+ // If we have an app token, we ask it to destroy the surface for us,
+ // so that it can take care to ensure the activity has actually stopped
+ // and the surface is not still in use. Otherwise we add the service to
+ // mDestroySurface and allow it to be processed in our next transaction.
+ if (mWin.mAppToken != null) {
+ if (hasSurface()) {
+ hide("finishExit");
+ }
+ mWin.mAppToken.destroySurfaces();
+ } else {
+ if (hasSurface()) {
+ mService.mDestroySurface.add(mWin);
+ hide("finishExit");
+ }
+ mWin.mExiting = false;
+ if (mWin.mRemoveOnExit) {
+ mService.mPendingRemove.add(mWin);
+ mWin.mRemoveOnExit = false;
+ }
}
- mWin.mExiting = false;
- if (mWin.mRemoveOnExit) {
- mService.mPendingRemove.add(mWin);
- mWin.mRemoveOnExit = false;
- }
+
mWallpaperControllerLocked.hideWallpapers(mWin);
}
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 2560c31..8d1b124 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -348,6 +348,11 @@
}
@Override
+ public void notifyAppStopped(IBinder token) throws RemoteException {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
public void setEventDispatching(boolean arg0) throws RemoteException {
// TODO Auto-generated method stub
}