| /* |
| * Copyright (C) 2013 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 com.android.ide.common.rendering; |
| |
| import static java.io.File.separator; |
| |
| import com.android.ide.common.res2.RecordingLogger; |
| import com.android.testutils.TestUtils; |
| import com.android.utils.SdkUtils; |
| import com.google.common.io.Files; |
| |
| import junit.framework.TestCase; |
| |
| import java.awt.image.BufferedImage; |
| import java.io.ByteArrayInputStream; |
| import java.io.File; |
| import java.io.FilePermission; |
| import java.io.InputStream; |
| import java.lang.reflect.Field; |
| import java.security.Permission; |
| import java.util.Collections; |
| import java.util.TimeZone; |
| import java.util.concurrent.BrokenBarrierException; |
| import java.util.concurrent.CyclicBarrier; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| import javax.imageio.ImageIO; |
| import javax.swing.*; |
| |
| public class RenderSecurityManagerTest extends TestCase { |
| |
| private Object myCredential = new Object(); |
| |
| public void testExec() throws Exception { |
| assertNull(RenderSecurityManager.getCurrent()); |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| RecordingLogger logger = new RecordingLogger(); |
| manager.setLogger(logger); |
| try { |
| assertNull(RenderSecurityManager.getCurrent()); |
| manager.setActive(true, myCredential); |
| assertSame(manager, RenderSecurityManager.getCurrent()); |
| if (new File("/bin/ls").exists()) { |
| Runtime.getRuntime().exec("/bin/ls"); |
| } else { |
| manager.checkExec("/bin/ls"); |
| } |
| fail("Should have thrown security exception"); |
| } catch (SecurityException exception) { |
| //noinspection ConstantConditions |
| assertEquals( |
| RenderSecurityManager.RESTRICT_READS ? |
| "Read access not allowed during rendering (/bin/ls)" : |
| "Exec access not allowed during rendering (/bin/ls)", |
| exception.toString()); |
| // pass |
| } finally { |
| manager.dispose(myCredential); |
| assertNull(RenderSecurityManager.getCurrent()); |
| assertNull(System.getSecurityManager()); |
| assertEquals(Collections.<String>emptyList(), logger.getWarningMsgs()); |
| } |
| } |
| |
| public void testSetSecurityManager() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setActive(true, myCredential); |
| System.setSecurityManager(null); |
| fail("Should have thrown security exception"); |
| } catch (SecurityException exception) { |
| assertEquals("Security access not allowed during rendering", exception.toString()); |
| // pass |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| public void testReadWrite() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setActive(true, myCredential); |
| manager.checkPermission(new FilePermission("/foo", "read,write")); |
| fail("Should have thrown security exception"); |
| } catch (SecurityException exception) { |
| assertEquals("Write access not allowed during rendering (/foo)", exception.toString()); |
| // pass |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| public void testExecute() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setActive(true, myCredential); |
| manager.checkPermission(new FilePermission("/foo", "execute")); |
| fail("Should have thrown security exception"); |
| } catch (SecurityException exception) { |
| assertEquals("Write access not allowed during rendering (/foo)", exception.toString()); |
| // pass |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| public void testDelete() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setActive(true, myCredential); |
| manager.checkPermission(new FilePermission("/foo", "delete")); |
| fail("Should have thrown security exception"); |
| } catch (SecurityException exception) { |
| assertEquals("Write access not allowed during rendering (/foo)", exception.toString()); |
| // pass |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| public void testLoadLibrary() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setActive(true, myCredential); |
| |
| // Unit test only runs on OSX |
| if (SdkUtils.startsWithIgnoreCase(System.getProperty("os.name"), "Mac") |
| && new File("/usr/lib/libc.dylib").exists()) { |
| System.load("/usr/lib/libc.dylib"); |
| fail("Should have thrown security exception"); |
| } |
| } catch (SecurityException exception) { |
| assertEquals("Link access not allowed during rendering (/usr/lib/libc.dylib)", |
| exception.toString()); |
| // pass |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| public void testAllowedLoadLibrary() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setActive(true, myCredential); |
| |
| System.loadLibrary("jsound"); |
| } catch (UnsatisfiedLinkError e) { |
| // pass - library may not be present on all JDKs |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| public void testInvalidRead() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setActive(true, myCredential); |
| |
| if (RenderSecurityManager.RESTRICT_READS) { |
| try { |
| File file = new File(System.getProperty("user.home")); |
| //noinspection ResultOfMethodCallIgnored |
| file.lastModified(); |
| |
| fail("Should have thrown security exception"); |
| } catch (SecurityException exception) { |
| assertEquals("Read access not allowed during rendering (" + |
| System.getProperty("user.home") + ")", exception.toString()); |
| // pass |
| } |
| } else { |
| try { |
| File file = new File(System.getProperty("user.home")); |
| //noinspection ResultOfMethodCallIgnored |
| file.lastModified(); |
| } catch (SecurityException exception) { |
| fail("Reading should be allowed"); |
| } |
| } |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| public void testInvalidPropertyWrite() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setActive(true, myCredential); |
| |
| // Try to make java.io.tmpdir point to user.home to grant myself access: |
| String userHome = System.getProperty("user.home"); |
| System.setProperty("java.io.tmpdir", userHome); |
| |
| fail("Should have thrown security exception"); |
| } catch (SecurityException exception) { |
| assertEquals("Write access not allowed during rendering (java.io.tmpdir)", |
| exception.toString()); |
| // pass |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| public void testReadOk() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setActive(true, myCredential); |
| |
| File jdkHome = new File(System.getProperty("java.home")); |
| assertTrue(jdkHome.exists()); |
| //noinspection ResultOfMethodCallIgnored |
| File[] files = jdkHome.listFiles(); |
| if (files != null) { |
| for (File file : files) { |
| if (file.isFile()) { |
| Files.toByteArray(file); |
| } |
| } |
| } |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| public void testProperties() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setActive(true, myCredential); |
| |
| System.getProperties(); |
| |
| fail("Should have thrown security exception"); |
| } catch (SecurityException exception) { |
| assertEquals("Property access not allowed during rendering", exception.toString()); |
| // pass |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| public void testExit() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setActive(true, myCredential); |
| |
| System.exit(-1); |
| |
| fail("Should have thrown security exception"); |
| } catch (SecurityException exception) { |
| assertEquals("Exit access not allowed during rendering (-1)", exception.toString()); |
| // pass |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| public void testThread() throws Exception { |
| final AtomicBoolean failedUnexpectedly = new AtomicBoolean(false); |
| Thread otherThread = new Thread("other") { |
| @Override |
| public void run() { |
| try { |
| assertNull(RenderSecurityManager.getCurrent()); |
| System.getProperties(); |
| } catch (SecurityException e) { |
| failedUnexpectedly.set(true); |
| } |
| } |
| }; |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setActive(true, myCredential); |
| |
| // Threads cloned from this one should inherit the same security constraints |
| final AtomicBoolean failedAsExpected = new AtomicBoolean(false); |
| final Thread renderThread = new Thread("render") { |
| @Override |
| public void run() { |
| try { |
| System.getProperties(); |
| } catch (SecurityException e) { |
| failedAsExpected.set(true); |
| } |
| } |
| }; |
| renderThread.start(); |
| renderThread.join(); |
| assertTrue(failedAsExpected.get()); |
| otherThread.start(); |
| otherThread.join(); |
| assertFalse(failedUnexpectedly.get()); |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| public void testActive() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setActive(true, myCredential); |
| |
| try { |
| System.getProperties(); |
| fail("Should have thrown security exception"); |
| } catch (SecurityException exception) { |
| // pass |
| } |
| |
| manager.setActive(false, myCredential); |
| |
| try { |
| System.getProperties(); |
| } catch (SecurityException exception) { |
| fail(exception.toString()); |
| } |
| |
| manager.setActive(true, myCredential); |
| |
| try { |
| System.getProperties(); |
| fail("Should have thrown security exception"); |
| } catch (SecurityException exception) { |
| // pass |
| } |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| public void testThread2() throws Exception { |
| // Check that when a new thread is created simultaneously from an unrelated |
| // thread during rendering, that new thread does not pick up the security manager. |
| // |
| final CyclicBarrier barrier1 = new CyclicBarrier(2); |
| final CyclicBarrier barrier2 = new CyclicBarrier(2); |
| final CyclicBarrier barrier3 = new CyclicBarrier(4); |
| final CyclicBarrier barrier4 = new CyclicBarrier(4); |
| final CyclicBarrier barrier5 = new CyclicBarrier(4); |
| final CyclicBarrier barrier6 = new CyclicBarrier(4); |
| |
| // First the threads reach barrier1. Then from barrier1 to barrier2, thread1 |
| // installs the security manager. Then from barrier2 to barrier3, thread2 |
| // checks that it does not have any security restrictions, and creates thread3. |
| // Thread1 will ensure that the security manager is working there, and it will |
| // create thread4. Then after barrier3 (where thread3 and thread4 are now also |
| // participating) thread3 will ensure that it too has no security restrictions, |
| // and thread4 will ensure that it does. At barrier4 the security manager gets |
| // uninstalled, and at barrier5 all threads will check that there are no more |
| // restrictions. At barrier6 all threads are done. |
| |
| final Thread thread1 = new Thread("render") { |
| @Override |
| public void run() { |
| try { |
| barrier1.await(); |
| assertNull(RenderSecurityManager.getCurrent()); |
| |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| manager.setActive(true, myCredential); |
| |
| barrier2.await(); |
| |
| Thread thread4 = new Thread() { |
| @Override |
| public void run() { |
| try { |
| barrier3.await(); |
| |
| try { |
| System.getProperties(); |
| fail("Should have thrown security exception"); |
| } catch (SecurityException e) { |
| // pass |
| } |
| |
| barrier4.await(); |
| barrier5.await(); |
| assertNull(RenderSecurityManager.getCurrent()); |
| assertNull(System.getSecurityManager()); |
| barrier6.await(); |
| } catch (InterruptedException e) { |
| fail(e.toString()); |
| } catch (BrokenBarrierException e) { |
| fail(e.toString()); |
| } |
| } |
| }; |
| thread4.start(); |
| |
| try { |
| System.getProperties(); |
| fail("Should have thrown security exception"); |
| } catch (SecurityException e) { |
| // expected |
| } |
| |
| barrier3.await(); |
| barrier4.await(); |
| manager.dispose(myCredential); |
| |
| assertNull(RenderSecurityManager.getCurrent()); |
| assertNull(System.getSecurityManager()); |
| |
| barrier5.await(); |
| barrier6.await(); |
| |
| } catch (InterruptedException e) { |
| fail(e.toString()); |
| } catch (BrokenBarrierException e) { |
| fail(e.toString()); |
| } |
| |
| } |
| }; |
| |
| final Thread thread2 = new Thread("unrelated") { |
| @Override |
| public void run() { |
| try { |
| barrier1.await(); |
| assertNull(RenderSecurityManager.getCurrent()); |
| barrier2.await(); |
| assertNull(RenderSecurityManager.getCurrent()); |
| assertNotNull(System.getSecurityManager()); |
| |
| try { |
| System.getProperties(); |
| } catch (SecurityException e) { |
| fail("Should not have been affected by security manager"); |
| } |
| |
| Thread thread3 = new Thread() { |
| @Override |
| public void run() { |
| try { |
| barrier3.await(); |
| |
| try { |
| System.getProperties(); |
| } catch (SecurityException e) { |
| fail("Should not have been affected by security manager"); |
| } |
| |
| barrier4.await(); |
| barrier5.await(); |
| assertNull(RenderSecurityManager.getCurrent()); |
| assertNull(System.getSecurityManager()); |
| barrier6.await(); |
| |
| } catch (InterruptedException e) { |
| fail(e.toString()); |
| } catch (BrokenBarrierException e) { |
| fail(e.toString()); |
| } |
| } |
| }; |
| thread3.start(); |
| |
| barrier3.await(); |
| barrier4.await(); |
| barrier5.await(); |
| assertNull(RenderSecurityManager.getCurrent()); |
| assertNull(System.getSecurityManager()); |
| barrier6.await(); |
| |
| } catch (InterruptedException e) { |
| fail(e.toString()); |
| } catch (BrokenBarrierException e) { |
| fail(e.toString()); |
| } |
| |
| } |
| }; |
| |
| thread1.start(); |
| thread2.start(); |
| thread1.join(); |
| thread2.join(); |
| } |
| |
| public void testDisabled() throws Exception { |
| assertNull(RenderSecurityManager.getCurrent()); |
| |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| RenderSecurityManager.sEnabled = false; |
| try { |
| assertNull(RenderSecurityManager.getCurrent()); |
| manager.setActive(true, myCredential); |
| assertSame(manager, System.getSecurityManager()); |
| if (new File("/bin/ls").exists()) { |
| Runtime.getRuntime().exec("/bin/ls"); |
| } else { |
| manager.checkExec("/bin/ls"); |
| } |
| } catch (SecurityException exception) { |
| fail("Should have been disabled"); |
| } finally { |
| RenderSecurityManager.sEnabled = true; |
| manager.dispose(myCredential); |
| assertNull(RenderSecurityManager.getCurrent()); |
| assertNull(System.getSecurityManager()); |
| } |
| } |
| |
| public void testLogger() throws Exception { |
| assertNull(RenderSecurityManager.getCurrent()); |
| |
| final CyclicBarrier barrier1 = new CyclicBarrier(2); |
| final CyclicBarrier barrier2 = new CyclicBarrier(2); |
| final CyclicBarrier barrier3 = new CyclicBarrier(2); |
| |
| Thread thread = new Thread() { |
| @Override |
| public void run() { |
| try { |
| barrier1.await(); |
| barrier2.await(); |
| |
| System.setSecurityManager(new SecurityManager() { |
| @Override |
| public String toString() { |
| return "MyTestSecurityManager"; |
| } |
| |
| @Override |
| public void checkPermission(Permission permission) { |
| } |
| }); |
| |
| barrier3.await(); |
| assertNull(RenderSecurityManager.getCurrent()); |
| assertNotNull(System.getSecurityManager()); |
| assertEquals("MyTestSecurityManager", System.getSecurityManager().toString()); |
| } catch (InterruptedException e) { |
| fail(e.toString()); |
| } catch (BrokenBarrierException e) { |
| fail(e.toString()); |
| } |
| } |
| }; |
| thread.start(); |
| |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| RecordingLogger logger = new RecordingLogger(); |
| manager.setLogger(logger); |
| try { |
| barrier1.await(); |
| assertNull(RenderSecurityManager.getCurrent()); |
| manager.setActive(true, myCredential); |
| assertSame(manager, RenderSecurityManager.getCurrent()); |
| barrier2.await(); |
| barrier3.await(); |
| |
| assertNull(RenderSecurityManager.getCurrent()); |
| manager.setActive(false, myCredential); |
| assertNull(RenderSecurityManager.getCurrent()); |
| |
| assertEquals(Collections.singletonList( |
| "RenderSecurityManager being replaced by another thread"), |
| logger.getWarningMsgs()); |
| } catch (InterruptedException e) { |
| fail(e.toString()); |
| } catch (BrokenBarrierException e) { |
| fail(e.toString()); |
| } finally { |
| manager.dispose(myCredential); |
| assertNull(RenderSecurityManager.getCurrent()); |
| assertNotNull(System.getSecurityManager()); |
| assertEquals("MyTestSecurityManager", System.getSecurityManager().toString()); |
| System.setSecurityManager(null); |
| } |
| } |
| |
| public void testEnterExitSafeRegion() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| Object credential = new Object(); |
| try { |
| manager.setActive(true, credential); |
| |
| boolean token = RenderSecurityManager.enterSafeRegion(credential); |
| manager.checkPermission(new FilePermission("/foo", "execute")); |
| RenderSecurityManager.exitSafeRegion(token); |
| |
| assertNotNull(RenderSecurityManager.getCurrent()); |
| boolean tokenOuter = RenderSecurityManager.enterSafeRegion(credential); |
| assertNull(RenderSecurityManager.getCurrent()); |
| boolean tokenInner = RenderSecurityManager.enterSafeRegion(credential); |
| assertNull(RenderSecurityManager.getCurrent()); |
| manager.checkPermission(new FilePermission("/foo", "execute")); |
| assertNull(RenderSecurityManager.getCurrent()); |
| manager.checkPermission(new FilePermission("/foo", "execute")); |
| RenderSecurityManager.exitSafeRegion(tokenInner); |
| assertNull(RenderSecurityManager.getCurrent()); |
| RenderSecurityManager.exitSafeRegion(tokenOuter); |
| assertNotNull(RenderSecurityManager.getCurrent()); |
| |
| // Wrong credential |
| Object wrongCredential = new Object(); |
| try { |
| token = RenderSecurityManager.enterSafeRegion(wrongCredential); |
| manager.checkPermission(new FilePermission("/foo", "execute")); |
| RenderSecurityManager.exitSafeRegion(token); |
| fail("Should have thrown exception"); |
| } catch (SecurityException e) { |
| // pass |
| } |
| |
| // Try turning off the security manager |
| try { |
| manager.setActive(false, wrongCredential); |
| } catch (SecurityException e) { |
| // pass |
| } |
| try { |
| manager.setActive(false, null); |
| } catch (SecurityException e) { |
| // pass |
| } |
| try { |
| manager.dispose(wrongCredential); |
| } catch (SecurityException e) { |
| // pass |
| } |
| |
| // Try looking up the secret |
| try { |
| Field field = RenderSecurityManager.class.getField("sCredential"); |
| field.setAccessible(true); |
| Object secret = field.get(null); |
| manager.dispose(secret); |
| fail("Shouldn't be able to find our way to the credential"); |
| } catch (Exception e) { |
| // pass |
| assertEquals("java.lang.NoSuchFieldException: sCredential", e.toString()); |
| } |
| |
| // Try looking up the secret (with getDeclaredField instead of getField) |
| try { |
| Field field = RenderSecurityManager.class.getDeclaredField("sCredential"); |
| field.setAccessible(true); |
| Object secret = field.get(null); |
| manager.dispose(secret); |
| fail("Shouldn't be able to find our way to the credential"); |
| } catch (Exception e) { |
| // pass |
| assertEquals("Reflection access not allowed during rendering " |
| + "(com.android.ide.common.rendering.RenderSecurityManager)", |
| e.toString()); |
| } |
| } finally { |
| manager.dispose(credential); |
| } |
| } |
| |
| public void testImageIo() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setActive(true, myCredential); |
| |
| File root = TestUtils.getRoot("resources", "baseMerge"); |
| assertNotNull(root); |
| assertTrue(root.exists()); |
| final File icon = new File(root, "overlay" + separator + "drawable" + separator |
| + "icon2.png"); |
| assertTrue(icon.exists()); |
| final byte[] buf = Files.toByteArray(icon); |
| InputStream stream = new ByteArrayInputStream(buf); |
| assertNotNull(stream); |
| BufferedImage image = ImageIO.read(stream); |
| assertNotNull(image); |
| assertNull(ImageIO.getCacheDirectory()); |
| |
| // Also run in non AWT thread to test ImageIO thread locals cache dir behavior |
| Thread thread = new Thread() { |
| @Override |
| public void run() { |
| try { |
| assertFalse(SwingUtilities.isEventDispatchThread()); |
| final byte[] buf = Files.toByteArray(icon); |
| InputStream stream = new ByteArrayInputStream(buf); |
| assertNotNull(stream); |
| BufferedImage image = ImageIO.read(stream); |
| assertNotNull(image); |
| assertNull(ImageIO.getCacheDirectory()); |
| } catch (Throwable t) { |
| t.printStackTrace(); |
| fail(t.toString()); |
| } |
| } |
| }; |
| |
| thread.start(); |
| thread.join(); |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| public void testTempDir() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setActive(true, myCredential); |
| |
| String temp = System.getProperty("java.io.tmpdir"); |
| assertNotNull(temp); |
| |
| manager.checkPermission(new FilePermission(temp, "read,write")); |
| manager.checkPermission(new FilePermission(temp + File.separator, "read,write")); |
| |
| temp = new File(temp).getCanonicalPath(); |
| manager.checkPermission(new FilePermission(temp, "read,write")); |
| |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| public void testAppTempDir() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setAppTempDir("/random/path/"); |
| manager.setActive(true, myCredential); |
| manager.checkPermission(new FilePermission("/random/path/myfile.tmp", "read,write")); |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| |
| public void testSetTimeZone() throws Exception { |
| RenderSecurityManager manager = new RenderSecurityManager(null, null); |
| try { |
| manager.setActive(true, myCredential); |
| |
| /* ICU needs this (needed for Calendar widget rendering) |
| at java.util.TimeZone.hasPermission(TimeZone.java:597) |
| at java.util.TimeZone.setDefault(TimeZone.java:619) |
| at com.ibm.icu.util.TimeZone.setDefault(TimeZone.java:973) |
| at libcore.icu.DateIntervalFormat_Delegate.createDateIntervalFormat(DateIntervalFormat_Delegate.java:61) |
| at libcore.icu.DateIntervalFormat.createDateIntervalFormat(DateIntervalFormat.java) |
| at libcore.icu.DateIntervalFormat.getFormatter(DateIntervalFormat.java:112) |
| at libcore.icu.DateIntervalFormat.formatDateRange(DateIntervalFormat.java:102) |
| at libcore.icu.DateIntervalFormat.formatDateRange(DateIntervalFormat.java:71) |
| at android.text.format.DateUtils.formatDateRange(DateUtils.java:826) |
| */ |
| TimeZone deflt = TimeZone.getDefault(); |
| String[] availableIDs = TimeZone.getAvailableIDs(); |
| TimeZone timeZone = TimeZone.getTimeZone(availableIDs[0]); |
| TimeZone.setDefault(timeZone); |
| TimeZone.setDefault(deflt); |
| } finally { |
| manager.dispose(myCredential); |
| } |
| } |
| |
| |
| } |