diff options
Diffstat (limited to 'core/tests')
24 files changed, 1125 insertions, 154 deletions
diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml index 1649268..6bd8f6e 100644 --- a/core/tests/ConnectivityManagerTest/AndroidManifest.xml +++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml @@ -68,8 +68,9 @@ <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> - <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <!-- This permission is added for API call setAirplaneMode() in ConnectivityManager --> diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java index 80d5668..64fed7f 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java @@ -49,7 +49,8 @@ import java.util.List; */ public class ConnectivityManagerTestBase extends InstrumentationTestCase { - private static final String PING_IP_ADDR = "8.8.8.8"; + private static final String[] PING_HOST_LIST = { + "www.google.com", "www.yahoo.com", "www.bing.com", "www.facebook.com", "www.ask.com"}; protected static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; //10 seconds protected static final int WIFI_SCAN_TIMEOUT = 50 * 1000; // 50 seconds @@ -281,22 +282,14 @@ public class ConnectivityManagerTestBase extends InstrumentationTestCase { } /** - * @param pingServerList a list of servers that can be used for ping test, can be null * @return true if the ping test is successful, false otherwise. */ - protected boolean pingTest(String[] pingServerList) { - String[] hostList = {"www.google.com", "www.yahoo.com", - "www.bing.com", "www.facebook.com", "www.ask.com"}; - if (pingServerList != null) { - hostList = pingServerList; - } - + protected boolean pingTest() { long startTime = System.currentTimeMillis(); while ((System.currentTimeMillis() - startTime) < PING_TIMER) { try { // assume the chance that all servers are down is very small - for (int i = 0; i < hostList.length; i++ ) { - String host = hostList[i]; + for (String host : PING_HOST_LIST) { logv("Start ping test, ping " + host); Process p = Runtime.getRuntime().exec("ping -c 10 -w 100 " + host); int status = p.waitFor(); @@ -312,6 +305,7 @@ public class ConnectivityManagerTestBase extends InstrumentationTestCase { } catch (InterruptedException e) { logv("Ping test Fail: InterruptedException"); } + SystemClock.sleep(SHORT_TIMEOUT); } // ping test timeout return false; @@ -458,14 +452,7 @@ public class ConnectivityManagerTestBase extends InstrumentationTestCase { // use ping request against Google public DNS to verify connectivity protected boolean checkNetworkConnectivity() { assertTrue("no active network connection", waitForActiveNetworkConnection(LONG_TIMEOUT)); - try { - Process proc = Runtime.getRuntime().exec(new String[]{ - "/system/bin/ping", "-W", "30", "-c", "1", PING_IP_ADDR}); - return proc.waitFor() == 0; - } catch (InterruptedException | IOException e) { - Log.e(mLogTag, "Ping failed", e); - } - return false; + return pingTest(); } @Override diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java index d5051df..2d291ff 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java @@ -72,16 +72,6 @@ public class ConnectivityManagerMobileTest extends ConnectivityManagerTestBase super.tearDown(); } - // help function to verify 3G connection - public void verifyCellularConnection() { - NetworkInfo extraNetInfo = mCm.getActiveNetworkInfo(); - assertEquals("network type is not MOBILE", ConnectivityManager.TYPE_MOBILE, - extraNetInfo.getType()); - assertTrue("not connected to cellular network", extraNetInfo.isConnected()); - } - - - // Test case 1: Test enabling Wifi without associating with any AP, no broadcast on network // event should be expected. @LargeTest @@ -336,4 +326,12 @@ public class ConnectivityManagerMobileTest extends ConnectivityManagerTestBase assertTrue("wifi state not disabled", waitForWifiState( WifiManager.WIFI_STATE_DISABLED, LONG_TIMEOUT)); } + + // help function to verify 3G connection + private void verifyCellularConnection() { + NetworkInfo extraNetInfo = mCm.getActiveNetworkInfo(); + assertEquals("network type is not MOBILE", ConnectivityManager.TYPE_MOBILE, + extraNetInfo.getType()); + assertTrue("not connected to cellular network", extraNetInfo.isConnected()); + } } diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java index 41f01e6..de934b9 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java @@ -112,7 +112,7 @@ public class WifiApStress extends ConnectivityManagerTestBase { } catch (Exception e) { // ignore } - assertTrue("no uplink data connection after Wi-Fi tethering", pingTest(null)); + assertTrue("no uplink data connection after Wi-Fi tethering", pingTest()); // disable wifi hotspot assertTrue("failed to disable wifi hotspot", mWifiManager.setWifiApEnabled(config, false)); diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java index fbd4669..e04c214 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java @@ -199,7 +199,7 @@ public class WifiStressTest extends ConnectivityManagerTestBase { Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0); // set wifi sleep policy to never on while in sleep Settings.Global.putInt(mRunner.getContext().getContentResolver(), - Settings.Global.WIFI_SLEEP_POLICY, Settings.Global.WIFI_SLEEP_POLICY_NEVER); + Settings.Global.WIFI_SLEEP_POLICY, Settings.Global.WIFI_SLEEP_POLICY_DEFAULT); // set idle timeout for wifi to 15s Settings.Global.putLong(mRunner.getContext().getContentResolver(), Settings.Global.WIFI_IDLE_MS, WIFI_IDLE_MS); @@ -216,7 +216,7 @@ public class WifiStressTest extends ConnectivityManagerTestBase { assertTrue("wifi not connected", waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, WIFI_CONNECTION_TIMEOUT)); // Run ping test to verify the data connection - assertTrue("Wi-Fi is connected, but no data connection.", pingTest(null)); + assertTrue("Wi-Fi is connected, but no data connection.", pingTest()); long i, sum = 0, avgReconnectTime = 0; for (i = 1; i <= mReconnectIterations; i++) { @@ -264,7 +264,7 @@ public class WifiStressTest extends ConnectivityManagerTestBase { } else { assertEquals("mobile not connected", State.CONNECTED, mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState()); - assertTrue("no connectivity over mobile", pingTest(null)); + assertTrue("no connectivity over mobile", pingTest()); } // Turn screen on again @@ -281,7 +281,7 @@ public class WifiStressTest extends ConnectivityManagerTestBase { avgReconnectTime = sum / i; logv("average reconnection time is: " + avgReconnectTime); - assertTrue("Reconnect to Wi-Fi network, but no data connection.", pingTest(null)); + assertTrue("Reconnect to Wi-Fi network, but no data connection.", pingTest()); } Bundle result = new Bundle(); result.putLong("actual-iterations", i - 1); diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index b524177..226717e 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1252,6 +1252,13 @@ </intent-filter> </activity> + <activity android:name="android.content.res.ResourceCacheActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" /> + </intent-filter> + </activity> + </application> <instrumentation android:name="android.test.InstrumentationTestRunner" diff --git a/core/tests/coretests/res/anim/reset_state_anim.xml b/core/tests/coretests/res/anim/reset_state_anim.xml index 918d0a3..4bbbe62 100644 --- a/core/tests/coretests/res/anim/reset_state_anim.xml +++ b/core/tests/coretests/res/anim/reset_state_anim.xml @@ -1,4 +1,18 @@ <?xml version="1.0"?> +<!-- Copyright (C) 2014 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. +--> <set xmlns:android="http://schemas.android.com/apk/res/android"> <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="0" android:valueType="floatType"/> <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="0" android:valueType="floatType"/> diff --git a/core/tests/coretests/res/anim/test_animator.xml b/core/tests/coretests/res/anim/test_animator.xml new file mode 100644 index 0000000..49afc3f --- /dev/null +++ b/core/tests/coretests/res/anim/test_animator.xml @@ -0,0 +1,22 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2014 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. +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- if you change this, you should also change AnimatorInflaterTest#testLoadAnimator--> + <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="0" android:valueType="floatType"/> + <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="1" android:valueType="floatType"/> + <objectAnimator android:propertyName="left" android:duration="100" android:valueTo="2" android:valueType="intType"/> +</set>
\ No newline at end of file diff --git a/core/tests/coretests/res/anim/test_state_anim.xml b/core/tests/coretests/res/anim/test_state_anim.xml index 9e08f68..b6a4822 100644 --- a/core/tests/coretests/res/anim/test_state_anim.xml +++ b/core/tests/coretests/res/anim/test_state_anim.xml @@ -1,4 +1,18 @@ <?xml version="1.0"?> +<!-- Copyright (C) 2014 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. +--> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"> <set> diff --git a/core/tests/coretests/res/values-land/dimens.xml b/core/tests/coretests/res/values-land/dimens.xml new file mode 100644 index 0000000..1ee9f1d --- /dev/null +++ b/core/tests/coretests/res/values-land/dimens.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 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. +--> +<resources> + <dimen name="resource_cache_test_orientation_dependent">3dp</dimen> +</resources>
\ No newline at end of file diff --git a/core/tests/coretests/res/values/dimens.xml b/core/tests/coretests/res/values/dimens.xml new file mode 100644 index 0000000..00fc414 --- /dev/null +++ b/core/tests/coretests/res/values/dimens.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 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. +--> +<resources> + <dimen name="resource_cache_test_generic">10dp</dimen> + <dimen name="resource_cache_test_orientation_dependent">20dp</dimen> +</resources>
\ No newline at end of file diff --git a/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java b/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java new file mode 100644 index 0000000..3c81853 --- /dev/null +++ b/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java @@ -0,0 +1,61 @@ +/* +* Copyright (C) 2014 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.animation; + +import android.test.ActivityInstrumentationTestCase2; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import com.android.frameworks.coretests.R; + +public class AnimatorInflaterTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> { + Set<Integer> identityHashes = new HashSet<Integer>(); + + public AnimatorInflaterTest() { + super(BasicAnimatorActivity.class); + } + + private void assertUnique(Object object) { + assertUnique(object, ""); + } + + private void assertUnique(Object object, String msg) { + final int code = System.identityHashCode(object); + assertTrue("object should be unique " + msg + ", obj:" + object, identityHashes.add(code)); + + } + + public void testLoadStateListAnimator() { + StateListAnimator sla1 = AnimatorInflater.loadStateListAnimator(getActivity(), + R.anim.test_state_anim); + sla1.setTarget(getActivity().mAnimatingButton); + StateListAnimator sla2 = AnimatorInflater.loadStateListAnimator(getActivity(), + R.anim.test_state_anim); + assertNull(sla2.getTarget()); + for (StateListAnimator sla : new StateListAnimator[]{sla1, sla2}) { + assertUnique(sla); + assertEquals(3, sla.getTuples().size()); + for (StateListAnimator.Tuple tuple : sla.getTuples()) { + assertUnique(tuple); + assertUnique(tuple.getAnimator()); + } + } + } + +} diff --git a/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java b/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java index 93808d9..6bcf8fc 100644 --- a/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java +++ b/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java @@ -19,11 +19,14 @@ import com.android.frameworks.coretests.R; import android.app.Activity; import android.os.Bundle; +import android.widget.Button; public class BasicAnimatorActivity extends Activity { + public Button mAnimatingButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.animator_basic); + mAnimatingButton = (Button) findViewById(R.id.animatingButton); } } diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index 3a80309..dc43a2f 100644 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -16,19 +16,20 @@ package android.content.pm; -import static android.system.OsConstants.*; - -import com.android.frameworks.coretests.R; -import com.android.internal.content.PackageHelper; +import static android.system.OsConstants.S_IFDIR; +import static android.system.OsConstants.S_IFMT; +import static android.system.OsConstants.S_IRGRP; +import static android.system.OsConstants.S_IROTH; +import static android.system.OsConstants.S_IRWXU; +import static android.system.OsConstants.S_ISDIR; +import static android.system.OsConstants.S_IXGRP; +import static android.system.OsConstants.S_IXOTH; import android.app.PackageInstallObserver; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.KeySet; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageParser.PackageParserException; import android.content.res.Resources; @@ -57,16 +58,17 @@ import android.system.StructStat; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.SmallTest; -import android.util.DisplayMetrics; import android.util.Log; +import com.android.frameworks.coretests.R; +import com.android.internal.content.PackageHelper; + import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.HashSet; import java.util.List; import java.util.Set; - import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -79,9 +81,7 @@ public class PackageManagerTests extends AndroidTestCase { public final long WAIT_TIME_INCR = 5 * 1000; - private static final String APP_LIB_DIR_PREFIX = "/data/app-lib/"; - - private static final String SECURE_CONTAINERS_PREFIX = "/mnt/asec/"; + private static final String SECURE_CONTAINERS_PREFIX = "/mnt/asec"; private static final int APP_INSTALL_AUTO = PackageHelper.APP_INSTALL_AUTO; @@ -128,7 +128,11 @@ public class PackageManagerTests extends AndroidTestCase { private boolean doneFlag = false; - public void packageInstalled(String packageName, Bundle extras, int returnCode) { + @Override + public void onPackageInstalled(String basePackageName, int returnCode, String msg, + Bundle extras) { + Log.d(TAG, "onPackageInstalled: code=" + returnCode + ", msg=" + msg + ", extras=" + + extras); synchronized (this) { this.returnCode = returnCode; doneFlag = true; @@ -410,10 +414,12 @@ public class PackageManagerTests extends AndroidTestCase { String appInstallPath = new File(dataDir, "app").getPath(); String drmInstallPath = new File(dataDir, "app-private").getPath(); File srcDir = new File(info.sourceDir); - String srcPath = srcDir.getParent(); + String srcPath = srcDir.getParentFile().getParent(); File publicSrcDir = new File(info.publicSourceDir); - String publicSrcPath = publicSrcDir.getParent(); + String publicSrcPath = publicSrcDir.getParentFile().getParent(); long pkgLen = new File(info.sourceDir).length(); + String expectedLibPath = new File(new File(info.sourceDir).getParentFile(), "lib") + .getPath(); int rLoc = getInstallLoc(flags, expInstallLocation, pkgLen); if (rLoc == INSTALL_LOC_INT) { @@ -436,12 +442,11 @@ public class PackageManagerTests extends AndroidTestCase { } } else { assertFalse((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0); - assertEquals(srcPath, appInstallPath); - assertEquals(publicSrcPath, appInstallPath); + assertEquals(appInstallPath, srcPath); + assertEquals(appInstallPath, publicSrcPath); assertStartsWith("Native library should point to shared lib directory", - new File(APP_LIB_DIR_PREFIX, info.packageName).getPath(), - info.nativeLibraryDir); - assertDirOwnerGroupPerms( + expectedLibPath, info.nativeLibraryDir); + assertDirOwnerGroupPermsIfExists( "Native library directory should be owned by system:system and 0755", Process.SYSTEM_UID, Process.SYSTEM_UID, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, @@ -451,13 +456,13 @@ public class PackageManagerTests extends AndroidTestCase { // Make sure the native library dir is not a symlink final File nativeLibDir = new File(info.nativeLibraryDir); - assertTrue("Native library dir should exist at " + info.nativeLibraryDir, - nativeLibDir.exists()); - try { - assertEquals("Native library dir should not be a symlink", - info.nativeLibraryDir, nativeLibDir.getCanonicalPath()); - } catch (IOException e) { - fail("Can't read " + nativeLibDir.getPath()); + if (nativeLibDir.exists()) { + try { + assertEquals("Native library dir should not be a symlink", + info.nativeLibraryDir, nativeLibDir.getCanonicalPath()); + } catch (IOException e) { + fail("Can't read " + nativeLibDir.getPath()); + } } } else if (rLoc == INSTALL_LOC_SD) { if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) { @@ -500,9 +505,13 @@ public class PackageManagerTests extends AndroidTestCase { } } - private void assertDirOwnerGroupPerms(String reason, int uid, int gid, int perms, String path) { - final StructStat stat; + private void assertDirOwnerGroupPermsIfExists(String reason, int uid, int gid, int perms, + String path) { + if (!new File(path).exists()) { + return; + } + final StructStat stat; try { stat = Os.lstat(path); } catch (ErrnoException e) { @@ -3007,7 +3016,7 @@ public class PackageManagerTests extends AndroidTestCase { @LargeTest public void testReplaceMatchNoCerts1() throws Exception { replaceCerts(APP1_CERT1_CERT2, APP1_CERT3, true, true, - PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES); + PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE); } /* @@ -3017,7 +3026,7 @@ public class PackageManagerTests extends AndroidTestCase { @LargeTest public void testReplaceMatchNoCerts2() throws Exception { replaceCerts(APP1_CERT1_CERT2, APP1_CERT3_CERT4, true, true, - PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES); + PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE); } /* @@ -3027,7 +3036,7 @@ public class PackageManagerTests extends AndroidTestCase { @LargeTest public void testReplaceMatchSomeCerts1() throws Exception { replaceCerts(APP1_CERT1_CERT2, APP1_CERT1, true, true, - PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES); + PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE); } /* @@ -3037,7 +3046,7 @@ public class PackageManagerTests extends AndroidTestCase { @LargeTest public void testReplaceMatchSomeCerts2() throws Exception { replaceCerts(APP1_CERT1_CERT2, APP1_CERT2, true, true, - PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES); + PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE); } /* @@ -3047,7 +3056,7 @@ public class PackageManagerTests extends AndroidTestCase { @LargeTest public void testReplaceMatchMoreCerts() throws Exception { replaceCerts(APP1_CERT1, APP1_CERT1_CERT2, true, true, - PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES); + PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE); } /* @@ -3058,7 +3067,7 @@ public class PackageManagerTests extends AndroidTestCase { @LargeTest public void testReplaceMatchMoreCertsReplaceSomeCerts() throws Exception { InstallParams ip = replaceCerts(APP1_CERT1, APP1_CERT1_CERT2, false, true, - PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES); + PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE); try { int rFlags = PackageManager.INSTALL_REPLACE_EXISTING; installFromRawResource("install.apk", APP1_CERT1, rFlags, false, @@ -3098,7 +3107,7 @@ public class PackageManagerTests extends AndroidTestCase { */ public void testUpgradeKSWithWrongKey() throws Exception { replaceCerts(R.raw.keyset_sa_ua, R.raw.keyset_sb_ua, true, true, - PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES); + PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE); } /* @@ -3107,7 +3116,7 @@ public class PackageManagerTests extends AndroidTestCase { */ public void testUpgradeKSWithWrongSigningKey() throws Exception { replaceCerts(R.raw.keyset_sa_ub, R.raw.keyset_sa_ub, true, true, - PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES); + PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE); } /* @@ -3139,7 +3148,7 @@ public class PackageManagerTests extends AndroidTestCase { */ public void testMultipleUpgradeKSWithSigningKey() throws Exception { replaceCerts(R.raw.keyset_sau_ub, R.raw.keyset_sa_ua, true, true, - PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES); + PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE); } /* @@ -3732,7 +3741,7 @@ public class PackageManagerTests extends AndroidTestCase { int apk2 = SHARED2_CERT1_CERT2; int rapk1 = SHARED1_CERT1; boolean fail = true; - int retCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; + int retCode = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH); installFromRawResource("install.apk", rapk1, PackageManager.INSTALL_REPLACE_EXISTING, true, fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); @@ -3744,7 +3753,7 @@ public class PackageManagerTests extends AndroidTestCase { int apk2 = SHARED2_CERT1_CERT2; int rapk2 = SHARED2_CERT1; boolean fail = true; - int retCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; + int retCode = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH); installFromRawResource("install.apk", rapk2, PackageManager.INSTALL_REPLACE_EXISTING, true, fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); @@ -3756,7 +3765,7 @@ public class PackageManagerTests extends AndroidTestCase { int apk2 = SHARED2_CERT1; int rapk1 = SHARED1_CERT2; boolean fail = true; - int retCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; + int retCode = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH); installFromRawResource("install.apk", rapk1, PackageManager.INSTALL_REPLACE_EXISTING, true, fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); @@ -3768,7 +3777,7 @@ public class PackageManagerTests extends AndroidTestCase { int apk2 = SHARED2_CERT1; int rapk2 = SHARED2_CERT2; boolean fail = true; - int retCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; + int retCode = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH); installFromRawResource("install.apk", rapk2, PackageManager.INSTALL_REPLACE_EXISTING, true, fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); @@ -3780,7 +3789,7 @@ public class PackageManagerTests extends AndroidTestCase { int apk2 = SHARED2_CERT1; int rapk1 = SHARED1_CERT1_CERT2; boolean fail = true; - int retCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; + int retCode = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH); installFromRawResource("install.apk", rapk1, PackageManager.INSTALL_REPLACE_EXISTING, true, fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); @@ -3792,7 +3801,7 @@ public class PackageManagerTests extends AndroidTestCase { int apk2 = SHARED2_CERT1; int rapk2 = SHARED2_CERT1_CERT2; boolean fail = true; - int retCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; + int retCode = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; checkSharedSignatures(apk1, apk2, false, false, -1, PackageManager.SIGNATURE_MATCH); installFromRawResource("install.apk", rapk2, PackageManager.INSTALL_REPLACE_EXISTING, true, fail, retCode, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); diff --git a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java new file mode 100644 index 0000000..e5a92bf --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java @@ -0,0 +1,263 @@ +package android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.List; + +public class ParceledListSliceTest extends TestCase { + + public void testSmallList() throws Exception { + final int objectCount = 100; + List<SmallObject> list = new ArrayList<SmallObject>(); + for (int i = 0; i < objectCount; i++) { + list.add(new SmallObject(i * 2, (i * 2) + 1)); + } + + ParceledListSlice<SmallObject> slice; + + Parcel parcel = Parcel.obtain(); + try { + parcel.writeParcelable(new ParceledListSlice<SmallObject>(list), 0); + parcel.setDataPosition(0); + slice = parcel.readParcelable(getClass().getClassLoader()); + } finally { + parcel.recycle(); + } + + assertNotNull(slice); + assertNotNull(slice.getList()); + assertEquals(objectCount, slice.getList().size()); + + for (int i = 0; i < objectCount; i++) { + assertEquals(i * 2, slice.getList().get(i).mFieldA); + assertEquals((i * 2) + 1, slice.getList().get(i).mFieldB); + } + } + + private static int measureLargeObject() { + Parcel p = Parcel.obtain(); + try { + new LargeObject(0, 0, 0, 0, 0).writeToParcel(p, 0); + return p.dataPosition(); + } finally { + p.recycle(); + } + } + + /** + * Test that when the list is large, the data is successfully parceled + * and unparceled (the implementation will send pieces of the list in + * separate round-trips to avoid the IPC limit). + */ + public void testLargeList() throws Exception { + final int thresholdBytes = 256 * 1024; + final int objectCount = thresholdBytes / measureLargeObject(); + + List<LargeObject> list = new ArrayList<LargeObject>(); + for (int i = 0; i < objectCount; i++) { + list.add(new LargeObject( + i * 5, + (i * 5) + 1, + (i * 5) + 2, + (i * 5) + 3, + (i * 5) + 4 + )); + } + + ParceledListSlice<LargeObject> slice; + + Parcel parcel = Parcel.obtain(); + try { + parcel.writeParcelable(new ParceledListSlice<LargeObject>(list), 0); + parcel.setDataPosition(0); + slice = parcel.readParcelable(getClass().getClassLoader()); + } finally { + parcel.recycle(); + } + + assertNotNull(slice); + assertNotNull(slice.getList()); + assertEquals(objectCount, slice.getList().size()); + + for (int i = 0; i < objectCount; i++) { + assertEquals(i * 5, slice.getList().get(i).mFieldA); + assertEquals((i * 5) + 1, slice.getList().get(i).mFieldB); + assertEquals((i * 5) + 2, slice.getList().get(i).mFieldC); + assertEquals((i * 5) + 3, slice.getList().get(i).mFieldD); + assertEquals((i * 5) + 4, slice.getList().get(i).mFieldE); + } + } + + /** + * Test that only homogeneous elements may be unparceled. + */ + public void testHomogeneousElements() throws Exception { + List<BaseObject> list = new ArrayList<BaseObject>(); + list.add(new LargeObject(0, 1, 2, 3, 4)); + list.add(new SmallObject(5, 6)); + list.add(new SmallObject(7, 8)); + + Parcel parcel = Parcel.obtain(); + try { + writeEvilParceledListSlice(parcel, list); + parcel.setDataPosition(0); + try { + ParceledListSlice.CREATOR.createFromParcel(parcel, getClass().getClassLoader()); + assertTrue("Unparceled heterogeneous ParceledListSlice", false); + } catch (IllegalArgumentException e) { + // Success, we're not allowed to process heterogeneous + // elements in a ParceledListSlice. + } + } finally { + parcel.recycle(); + } + } + + /** + * Write a ParcelableListSlice that uses the BaseObject base class as the Creator. + * This is dangerous, as it may affect how the data is unparceled, then later parceled + * by the system, leading to a self-modifying data security vulnerability. + */ + private static <T extends BaseObject> void writeEvilParceledListSlice(Parcel dest, List<T> list) { + final int listCount = list.size(); + + // Number of items. + dest.writeInt(listCount); + + // The type/creator to use when unparceling. Here we use the base class + // to simulate an attack on ParceledListSlice. + dest.writeString(BaseObject.class.getName()); + + for (int i = 0; i < listCount; i++) { + // 1 means the item is present. + dest.writeInt(1); + list.get(i).writeToParcel(dest, 0); + } + } + + public abstract static class BaseObject implements Parcelable { + protected static final int TYPE_SMALL = 0; + protected static final int TYPE_LARGE = 1; + + protected void writeToParcel(Parcel dest, int flags, int type) { + dest.writeInt(type); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * This is *REALLY* bad, but we're doing it in the test to ensure that we handle + * the possible exploit when unparceling an object with the BaseObject written as + * Creator. + */ + public static final Creator<BaseObject> CREATOR = new Creator<BaseObject>() { + @Override + public BaseObject createFromParcel(Parcel source) { + switch (source.readInt()) { + case TYPE_SMALL: + return SmallObject.createFromParcelBody(source); + case TYPE_LARGE: + return LargeObject.createFromParcelBody(source); + default: + throw new IllegalArgumentException("Unknown type"); + } + } + + @Override + public BaseObject[] newArray(int size) { + return new BaseObject[size]; + } + }; + } + + public static class SmallObject extends BaseObject { + public int mFieldA; + public int mFieldB; + + public SmallObject(int a, int b) { + mFieldA = a; + mFieldB = b; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags, TYPE_SMALL); + dest.writeInt(mFieldA); + dest.writeInt(mFieldB); + } + + public static SmallObject createFromParcelBody(Parcel source) { + return new SmallObject(source.readInt(), source.readInt()); + } + + public static final Creator<SmallObject> CREATOR = new Creator<SmallObject>() { + @Override + public SmallObject createFromParcel(Parcel source) { + // Consume the type (as it is always written out). + source.readInt(); + return createFromParcelBody(source); + } + + @Override + public SmallObject[] newArray(int size) { + return new SmallObject[size]; + } + }; + } + + public static class LargeObject extends BaseObject { + public int mFieldA; + public int mFieldB; + public int mFieldC; + public int mFieldD; + public int mFieldE; + + public LargeObject(int a, int b, int c, int d, int e) { + mFieldA = a; + mFieldB = b; + mFieldC = c; + mFieldD = d; + mFieldE = e; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags, TYPE_LARGE); + dest.writeInt(mFieldA); + dest.writeInt(mFieldB); + dest.writeInt(mFieldC); + dest.writeInt(mFieldD); + dest.writeInt(mFieldE); + } + + public static LargeObject createFromParcelBody(Parcel source) { + return new LargeObject( + source.readInt(), + source.readInt(), + source.readInt(), + source.readInt(), + source.readInt() + ); + } + + public static final Creator<LargeObject> CREATOR = new Creator<LargeObject>() { + @Override + public LargeObject createFromParcel(Parcel source) { + // Consume the type (as it is always written out). + source.readInt(); + return createFromParcelBody(source); + } + + @Override + public LargeObject[] newArray(int size) { + return new LargeObject[size]; + } + }; + } +} diff --git a/core/tests/coretests/src/android/content/pm/SignatureTest.java b/core/tests/coretests/src/android/content/pm/SignatureTest.java new file mode 100644 index 0000000..89d5997 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/SignatureTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 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.content.pm; + +import junit.framework.TestCase; + +public class SignatureTest extends TestCase { + + /** Cert A with valid syntax */ + private static final Signature A = new Signature("308201D33082013CA0030201020219373565373461363A31336534333439623635343A2D38303030300D06092A864886F70D01010505003017311530130603550403130C6269736F6E416E64726F6964301E170D3133303432343232323134345A170D3338303432353232323134345A3017311530130603550403130C6269736F6E416E64726F696430819F300D06092A864886F70D010101050003818D00308189028181009214CE08563B77FF3128D3A303254287301263A842D19D5D4EAF024EBEDF864F3802C215B2F3EA85432F3EFF1DB8F591B0854FA7C1C6E4A8A85132FA762CC2D12A8EBD34D8B15C241A91716577F03BB3D2AFFC24367AB1E5E03C387891E34E646E47FAD75B178C1FD077B9199B3ABA6D48E2464801F6592E98245124046E51A90203010001A317301530130603551D25040C300A06082B06010505070303300D06092A864886F70D0101050500038181000B71581EDDC20E8C18C1C140BEE72501A97E04CA12030C51D4C38767B6A9FB5155CF4858C565EF77E5E2C22687C1AAB04BBA2B81C9A73CFB8DE118B624094AAE43D8FC2D585D90839DAFA5033AF7B8C0DE27E6ADAE44C40508CE493E9C80F1F5DA9EC87ECA1844BAB12C83CC8EB5937E1BE36A42CD22086A826E00FB763CD577"); + /** Cert A with malformed syntax */ + private static final Signature M = new Signature("308201D43082013CA0030201020219373565373461363A31336534333439623635343A2D38303030300D06092A864886F70D01010505003017311530130603550403130C6269736F6E416E64726F6964301E170D3133303432343232323134345A170D3338303432353232323134345A3017311530130603550403130C6269736F6E416E64726F696430819F300D06092A864886F70D010101050003818D00308189028181009214CE08563B77FF3128D3A303254287301263A842D19D5D4EAF024EBEDF864F3802C215B2F3EA85432F3EFF1DB8F591B0854FA7C1C6E4A8A85132FA762CC2D12A8EBD34D8B15C241A91716577F03BB3D2AFFC24367AB1E5E03C387891E34E646E47FAD75B178C1FD077B9199B3ABA6D48E2464801F6592E98245124046E51A90203010001A317301530130603551D25040C300A06082B06010505070303300D06092A864886F70D010105050003820081000B71581EDDC20E8C18C1C140BEE72501A97E04CA12030C51D4C38767B6A9FB5155CF4858C565EF77E5E2C22687C1AAB04BBA2B81C9A73CFB8DE118B624094AAE43D8FC2D585D90839DAFA5033AF7B8C0DE27E6ADAE44C40508CE493E9C80F1F5DA9EC87ECA1844BAB12C83CC8EB5937E1BE36A42CD22086A826E00FB763CD577"); + /** Cert B with valid syntax */ + private static final Signature B = new Signature("308204a830820390a003020102020900a1573d0f45bea193300d06092a864886f70d0101050500308194310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550407130d4d6f756e7461696e20566965773110300e060355040a1307416e64726f69643110300e060355040b1307416e64726f69643110300e06035504031307416e64726f69643122302006092a864886f70d0109011613616e64726f696440616e64726f69642e636f6d301e170d3131303931393138343232355a170d3339303230343138343232355a308194310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550407130d4d6f756e7461696e20566965773110300e060355040a1307416e64726f69643110300e060355040b1307416e64726f69643110300e06035504031307416e64726f69643122302006092a864886f70d0109011613616e64726f696440616e64726f69642e636f6d30820120300d06092a864886f70d01010105000382010d00308201080282010100de1b51336afc909d8bcca5920fcdc8940578ec5c253898930e985481cfdea75ba6fc54b1f7bb492a03d98db471ab4200103a8314e60ee25fef6c8b83bc1b2b45b084874cffef148fa2001bb25c672b6beba50b7ac026b546da762ea223829a22b80ef286131f059d2c9b4ca71d54e515a8a3fd6bf5f12a2493dfc2619b337b032a7cf8bbd34b833f2b93aeab3d325549a93272093943bb59dfc0197ae4861ff514e019b73f5cf10023ad1a032adb4b9bbaeb4debecb4941d6a02381f1165e1ac884c1fca9525c5854dce2ad8ec839b8ce78442c16367efc07778a337d3ca2cdf9792ac722b95d67c345f1c00976ec372f02bfcbef0262cc512a6845e71cfea0d020103a381fc3081f9301d0603551d0e0416041478a0fc4517fb70ff52210df33c8d32290a44b2bb3081c90603551d230481c13081be801478a0fc4517fb70ff52210df33c8d32290a44b2bba1819aa48197308194310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550407130d4d6f756e7461696e20566965773110300e060355040a1307416e64726f69643110300e060355040b1307416e64726f69643110300e06035504031307416e64726f69643122302006092a864886f70d0109011613616e64726f696440616e64726f69642e636f6d820900a1573d0f45bea193300c0603551d13040530030101ff300d06092a864886f70d01010505000382010100977302dfbf668d7c61841c9c78d2563bcda1b199e95e6275a799939981416909722713531157f3cdcfea94eea7bb79ca3ca972bd8058a36ad1919291df42d7190678d4ea47a4b9552c9dfb260e6d0d9129b44615cd641c1080580e8a990dd768c6ab500c3b964e185874e4105109d94c5bd8c405deb3cf0f7960a563bfab58169a956372167a7e2674a04c4f80015d8f7869a7a4139aecbbdca2abc294144ee01e4109f0e47a518363cf6e9bf41f7560e94bdd4a5d085234796b05c7a1389adfd489feec2a107955129d7991daa49afb3d327dc0dc4fe959789372b093a89c8dbfa41554f771c18015a6cb242a17e04d19d55d3b4664eae12caf2a11cd2b836e"); + + public void testExactlyEqual() throws Exception { + assertTrue(Signature.areExactMatch(asArray(A), asArray(A))); + assertTrue(Signature.areExactMatch(asArray(M), asArray(M))); + + assertFalse(Signature.areExactMatch(asArray(A), asArray(B))); + assertFalse(Signature.areExactMatch(asArray(A), asArray(M))); + assertFalse(Signature.areExactMatch(asArray(M), asArray(A))); + + assertTrue(Signature.areExactMatch(asArray(A, M), asArray(M, A))); + } + + public void testEffectiveMatch() throws Exception { + assertTrue(Signature.areEffectiveMatch(asArray(A), asArray(A))); + assertTrue(Signature.areEffectiveMatch(asArray(M), asArray(M))); + + assertFalse(Signature.areEffectiveMatch(asArray(A), asArray(B))); + assertTrue(Signature.areEffectiveMatch(asArray(A), asArray(M))); + assertTrue(Signature.areEffectiveMatch(asArray(M), asArray(A))); + + assertTrue(Signature.areEffectiveMatch(asArray(A, M), asArray(M, A))); + assertTrue(Signature.areEffectiveMatch(asArray(A, B), asArray(M, B))); + assertFalse(Signature.areEffectiveMatch(asArray(A, M), asArray(A, B))); + } + + private static Signature[] asArray(Signature... s) { + return s; + } +} diff --git a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java new file mode 100644 index 0000000..e9fd5fb --- /dev/null +++ b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2014 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.content.res; + +import android.test.ActivityInstrumentationTestCase2; +import android.util.TypedValue; + +import com.android.frameworks.coretests.R; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class ConfigurationBoundResourceCacheTest + extends ActivityInstrumentationTestCase2<ResourceCacheActivity> { + + ConfigurationBoundResourceCache<Float> mCache; + + Method mCalcConfigChanges; + + public ConfigurationBoundResourceCacheTest() { + super(ResourceCacheActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mCache = new ConfigurationBoundResourceCache<Float>(getActivity().getResources()); + } + + public void testGetEmpty() { + assertNull(mCache.get(-1, null)); + } + + public void testSetGet() { + mCache.put(1, null, new DummyFloatConstantState(5f)); + assertEquals(5f, mCache.get(1, null)); + assertNotSame(5f, mCache.get(1, null)); + assertEquals(null, mCache.get(1, getActivity().getTheme())); + } + + public void testSetGetThemed() { + mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f)); + assertEquals(null, mCache.get(1, null)); + assertEquals(5f, mCache.get(1, getActivity().getTheme())); + assertNotSame(5f, mCache.get(1, getActivity().getTheme())); + } + + public void testMultiThreadPutGet() { + mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f)); + mCache.put(1, null, new DummyFloatConstantState(10f)); + assertEquals(10f, mCache.get(1, null)); + assertNotSame(10f, mCache.get(1, null)); + assertEquals(5f, mCache.get(1, getActivity().getTheme())); + assertNotSame(5f, mCache.get(1, getActivity().getTheme())); + } + + public void testVoidConfigChange() + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + TypedValue staticValue = new TypedValue(); + long key = 3L; + final Resources res = getActivity().getResources(); + res.getValue(R.dimen.resource_cache_test_generic, staticValue, true); + float staticDim = TypedValue.complexToDimension(staticValue.data, res.getDisplayMetrics()); + mCache.put(key, getActivity().getTheme(), + new DummyFloatConstantState(staticDim, staticValue.changingConfigurations)); + final Configuration cfg = res.getConfiguration(); + Configuration newCnf = new Configuration(cfg); + newCnf.orientation = cfg.orientation == Configuration.ORIENTATION_LANDSCAPE ? + Configuration.ORIENTATION_PORTRAIT + : Configuration.ORIENTATION_LANDSCAPE; + int changes = calcConfigChanges(res, newCnf); + assertEquals(staticDim, mCache.get(key, getActivity().getTheme())); + mCache.onConfigurationChange(changes); + assertEquals(staticDim, mCache.get(key, getActivity().getTheme())); + } + + public void testEffectiveConfigChange() + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + TypedValue changingValue = new TypedValue(); + long key = 4L; + final Resources res = getActivity().getResources(); + res.getValue(R.dimen.resource_cache_test_orientation_dependent, changingValue, true); + float changingDim = TypedValue.complexToDimension(changingValue.data, + res.getDisplayMetrics()); + mCache.put(key, getActivity().getTheme(), + new DummyFloatConstantState(changingDim, changingValue.changingConfigurations)); + + final Configuration cfg = res.getConfiguration(); + Configuration newCnf = new Configuration(cfg); + newCnf.orientation = cfg.orientation == Configuration.ORIENTATION_LANDSCAPE ? + Configuration.ORIENTATION_PORTRAIT + : Configuration.ORIENTATION_LANDSCAPE; + int changes = calcConfigChanges(res, newCnf); + assertEquals(changingDim, mCache.get(key, getActivity().getTheme())); + mCache.onConfigurationChange(changes); + assertNull(mCache.get(key, getActivity().getTheme())); + } + + public void testConfigChangeMultipleResources() + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + TypedValue staticValue = new TypedValue(); + TypedValue changingValue = new TypedValue(); + final Resources res = getActivity().getResources(); + res.getValue(R.dimen.resource_cache_test_generic, staticValue, true); + res.getValue(R.dimen.resource_cache_test_orientation_dependent, changingValue, true); + float staticDim = TypedValue.complexToDimension(staticValue.data, res.getDisplayMetrics()); + float changingDim = TypedValue.complexToDimension(changingValue.data, + res.getDisplayMetrics()); + mCache.put(R.dimen.resource_cache_test_generic, getActivity().getTheme(), + new DummyFloatConstantState(staticDim, staticValue.changingConfigurations)); + mCache.put(R.dimen.resource_cache_test_orientation_dependent, getActivity().getTheme(), + new DummyFloatConstantState(changingDim, changingValue.changingConfigurations)); + final Configuration cfg = res.getConfiguration(); + Configuration newCnf = new Configuration(cfg); + newCnf.orientation = cfg.orientation == Configuration.ORIENTATION_LANDSCAPE ? + Configuration.ORIENTATION_PORTRAIT + : Configuration.ORIENTATION_LANDSCAPE; + int changes = calcConfigChanges(res, newCnf); + assertEquals(staticDim, mCache.get(R.dimen.resource_cache_test_generic, + getActivity().getTheme())); + assertEquals(changingDim, mCache.get(R.dimen.resource_cache_test_orientation_dependent, + getActivity().getTheme())); + mCache.onConfigurationChange(changes); + assertEquals(staticDim, mCache.get(R.dimen.resource_cache_test_generic, + getActivity().getTheme())); + assertNull(mCache.get(R.dimen.resource_cache_test_orientation_dependent, + getActivity().getTheme())); + } + + public void testConfigChangeMultipleThemes() + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + TypedValue[] staticValues = new TypedValue[]{new TypedValue(), new TypedValue()}; + TypedValue[] changingValues = new TypedValue[]{new TypedValue(), new TypedValue()}; + float staticDim = 0; + float changingDim = 0; + final Resources res = getActivity().getResources(); + for (int i = 0; i < 2; i++) { + res.getValue(R.dimen.resource_cache_test_generic, staticValues[i], true); + staticDim = TypedValue + .complexToDimension(staticValues[i].data, res.getDisplayMetrics()); + + res.getValue(R.dimen.resource_cache_test_orientation_dependent, changingValues[i], + true); + changingDim = TypedValue.complexToDimension(changingValues[i].data, + res.getDisplayMetrics()); + final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null; + mCache.put(R.dimen.resource_cache_test_generic, theme, + new DummyFloatConstantState(staticDim, staticValues[i].changingConfigurations)); + mCache.put(R.dimen.resource_cache_test_orientation_dependent, theme, + new DummyFloatConstantState(changingDim, + changingValues[i].changingConfigurations)); + } + final Configuration cfg = res.getConfiguration(); + Configuration newCnf = new Configuration(cfg); + newCnf.orientation = cfg.orientation == Configuration.ORIENTATION_LANDSCAPE ? + Configuration.ORIENTATION_PORTRAIT + : Configuration.ORIENTATION_LANDSCAPE; + int changes = calcConfigChanges(res, newCnf); + for (int i = 0; i < 2; i++) { + final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null; + assertEquals(staticDim, mCache.get(R.dimen.resource_cache_test_generic, theme)); + assertEquals(changingDim, + mCache.get(R.dimen.resource_cache_test_orientation_dependent, theme)); + } + mCache.onConfigurationChange(changes); + for (int i = 0; i < 2; i++) { + final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null; + assertEquals(staticDim, mCache.get(R.dimen.resource_cache_test_generic, theme)); + assertNull(mCache.get(R.dimen.resource_cache_test_orientation_dependent, theme)); + } + } + + private int calcConfigChanges(Resources resources, Configuration configuration) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + if (mCalcConfigChanges == null) { + mCalcConfigChanges = Resources.class.getDeclaredMethod("calcConfigChanges", + Configuration.class); + mCalcConfigChanges.setAccessible(true); + } + return (Integer) mCalcConfigChanges.invoke(resources, configuration); + + } + + static class DummyFloatConstantState extends + ConstantState<Float> { + + final Float mObj; + + int mChangingConf = 0; + + DummyFloatConstantState(Float obj) { + mObj = obj; + } + + DummyFloatConstantState(Float obj, int changingConf) { + mObj = obj; + mChangingConf = changingConf; + } + + @Override + public int getChangingConfigurations() { + return mChangingConf; + } + + @Override + public Float newInstance() { + return new Float(mObj); + } + } +} diff --git a/core/tests/coretests/src/android/content/res/ResourceCacheActivity.java b/core/tests/coretests/src/android/content/res/ResourceCacheActivity.java new file mode 100644 index 0000000..f37e549 --- /dev/null +++ b/core/tests/coretests/src/android/content/res/ResourceCacheActivity.java @@ -0,0 +1,37 @@ +/* +* Copyright (C) 2014 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.content.res; + +import android.annotation.Nullable; +import android.app.Activity; +import android.os.Bundle; + +import java.lang.ref.WeakReference; + +public class ResourceCacheActivity extends Activity { + static WeakReference<ResourceCacheActivity> lastCreatedInstance; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + lastCreatedInstance = new WeakReference<ResourceCacheActivity>(this); + } + + public static ResourceCacheActivity getLastCreatedInstance() { + return lastCreatedInstance == null ? null : lastCreatedInstance.get(); + } +} diff --git a/core/tests/coretests/src/android/net/LinkAddressTest.java b/core/tests/coretests/src/android/net/LinkAddressTest.java index 7bc3974..adf8d95 100644 --- a/core/tests/coretests/src/android/net/LinkAddressTest.java +++ b/core/tests/coretests/src/android/net/LinkAddressTest.java @@ -33,8 +33,11 @@ import android.test.AndroidTestCase; import static android.test.MoreAsserts.assertNotEqual; import android.test.suitebuilder.annotation.SmallTest; +import static android.system.OsConstants.IFA_F_DADFAILED; import static android.system.OsConstants.IFA_F_DEPRECATED; +import static android.system.OsConstants.IFA_F_OPTIMISTIC; import static android.system.OsConstants.IFA_F_PERMANENT; +import static android.system.OsConstants.IFA_F_TEMPORARY; import static android.system.OsConstants.IFA_F_TENTATIVE; import static android.system.OsConstants.RT_SCOPE_HOST; import static android.system.OsConstants.RT_SCOPE_LINK; @@ -340,4 +343,73 @@ public class LinkAddressTest extends AndroidTestCase { l = new LinkAddress(V4 + "/28", IFA_F_PERMANENT, RT_SCOPE_LINK); assertParcelingIsLossless(l); } + + private void assertGlobalPreferred(LinkAddress l, String msg) { + assertTrue(msg, l.isGlobalPreferred()); + } + + private void assertNotGlobalPreferred(LinkAddress l, String msg) { + assertFalse(msg, l.isGlobalPreferred()); + } + + public void testIsGlobalPreferred() { + LinkAddress l; + + l = new LinkAddress(V4_ADDRESS, 32, 0, RT_SCOPE_UNIVERSE); + assertGlobalPreferred(l, "v4,global,noflags"); + + l = new LinkAddress("10.10.1.7/23", 0, RT_SCOPE_UNIVERSE); + assertGlobalPreferred(l, "v4-rfc1918,global,noflags"); + + l = new LinkAddress("10.10.1.7/23", 0, RT_SCOPE_SITE); + assertNotGlobalPreferred(l, "v4-rfc1918,site-local,noflags"); + + l = new LinkAddress("127.0.0.7/8", 0, RT_SCOPE_HOST); + assertNotGlobalPreferred(l, "v4-localhost,node-local,noflags"); + + l = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_UNIVERSE); + assertGlobalPreferred(l, "v6,global,noflags"); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_PERMANENT, RT_SCOPE_UNIVERSE); + assertGlobalPreferred(l, "v6,global,permanent"); + + // IPv6 ULAs are not acceptable "global preferred" addresses. + l = new LinkAddress("fc12::1/64", 0, RT_SCOPE_UNIVERSE); + assertNotGlobalPreferred(l, "v6,ula1,noflags"); + + l = new LinkAddress("fd34::1/64", 0, RT_SCOPE_UNIVERSE); + assertNotGlobalPreferred(l, "v6,ula2,noflags"); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_TEMPORARY, RT_SCOPE_UNIVERSE); + assertGlobalPreferred(l, "v6,global,tempaddr"); + + l = new LinkAddress(V6_ADDRESS, 64, (IFA_F_TEMPORARY|IFA_F_DADFAILED), + RT_SCOPE_UNIVERSE); + assertNotGlobalPreferred(l, "v6,global,tempaddr+dadfailed"); + + l = new LinkAddress(V6_ADDRESS, 64, (IFA_F_TEMPORARY|IFA_F_DEPRECATED), + RT_SCOPE_UNIVERSE); + assertNotGlobalPreferred(l, "v6,global,tempaddr+deprecated"); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_TEMPORARY, RT_SCOPE_SITE); + assertNotGlobalPreferred(l, "v6,site-local,tempaddr"); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_TEMPORARY, RT_SCOPE_LINK); + assertNotGlobalPreferred(l, "v6,link-local,tempaddr"); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_TEMPORARY, RT_SCOPE_HOST); + assertNotGlobalPreferred(l, "v6,node-local,tempaddr"); + + l = new LinkAddress("::1/128", IFA_F_PERMANENT, RT_SCOPE_HOST); + assertNotGlobalPreferred(l, "v6-localhost,node-local,permanent"); + + l = new LinkAddress(V6_ADDRESS, 64, (IFA_F_TEMPORARY|IFA_F_TENTATIVE), + RT_SCOPE_UNIVERSE); + assertNotGlobalPreferred(l, "v6,global,tempaddr+tentative"); + + l = new LinkAddress(V6_ADDRESS, 64, + (IFA_F_TEMPORARY|IFA_F_TENTATIVE|IFA_F_OPTIMISTIC), + RT_SCOPE_UNIVERSE); + assertGlobalPreferred(l, "v6,global,tempaddr+optimistic"); + } } diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java index 4015b3d..abfed6e 100644 --- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java +++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java @@ -31,8 +31,10 @@ public class LinkPropertiesTest extends TestCase { "2001:0db8:85a3:0000:0000:8a2e:0370:7334"); private static InetAddress DNS1 = NetworkUtils.numericToInetAddress("75.208.7.1"); private static InetAddress DNS2 = NetworkUtils.numericToInetAddress("69.78.7.1"); + private static InetAddress DNS6 = NetworkUtils.numericToInetAddress("2001:4860:4860::8888"); private static InetAddress GATEWAY1 = NetworkUtils.numericToInetAddress("75.208.8.1"); private static InetAddress GATEWAY2 = NetworkUtils.numericToInetAddress("69.78.8.1"); + private static InetAddress GATEWAY6 = NetworkUtils.numericToInetAddress("fe80::6:0000:613"); private static String NAME = "qmi0"; private static int MTU = 1500; @@ -338,14 +340,14 @@ public class LinkPropertiesTest extends TestCase { assertFalse("newname".equals(link.getInterfaceName())); } - assertTrue(rmnet0.removeStackedLink(clat4)); + assertTrue(rmnet0.removeStackedLink("clat4")); assertEquals(0, rmnet0.getStackedLinks().size()); assertEquals(1, rmnet0.getAddresses().size()); assertEquals(1, rmnet0.getLinkAddresses().size()); assertEquals(1, rmnet0.getAllAddresses().size()); assertEquals(1, rmnet0.getAllLinkAddresses().size()); - assertFalse(rmnet0.removeStackedLink(clat4)); + assertFalse(rmnet0.removeStackedLink("clat4")); } private LinkAddress getFirstLinkAddress(LinkProperties lp) { @@ -370,7 +372,7 @@ public class LinkPropertiesTest extends TestCase { assertTrue(stacked.hasGlobalIPv6Address()); assertFalse(lp.hasIPv4Address()); assertFalse(lp.hasGlobalIPv6Address()); - lp.removeStackedLink(stacked); + lp.removeStackedLink("stacked"); assertFalse(lp.hasIPv4Address()); assertFalse(lp.hasGlobalIPv6Address()); @@ -453,4 +455,47 @@ public class LinkPropertiesTest extends TestCase { lp2.setLinkAddresses(lp.getLinkAddresses()); assertTrue(lp.equals(lp)); } + + @SmallTest + public void testIsProvisioned() { + LinkProperties lp4 = new LinkProperties(); + assertFalse("v4only:empty", lp4.isProvisioned()); + lp4.addLinkAddress(LINKADDRV4); + assertFalse("v4only:addr-only", lp4.isProvisioned()); + lp4.addDnsServer(DNS1); + assertFalse("v4only:addr+dns", lp4.isProvisioned()); + lp4.addRoute(new RouteInfo(GATEWAY1)); + assertTrue("v4only:addr+dns+route", lp4.isProvisioned()); + + LinkProperties lp6 = new LinkProperties(); + assertFalse("v6only:empty", lp6.isProvisioned()); + lp6.addLinkAddress(LINKADDRV6LINKLOCAL); + assertFalse("v6only:fe80-only", lp6.isProvisioned()); + lp6.addDnsServer(DNS6); + assertFalse("v6only:fe80+dns", lp6.isProvisioned()); + lp6.addRoute(new RouteInfo(GATEWAY6)); + assertFalse("v6only:fe80+dns+route", lp6.isProvisioned()); + lp6.addLinkAddress(LINKADDRV6); + assertTrue("v6only:fe80+global+dns+route", lp6.isProvisioned()); + lp6.removeLinkAddress(LINKADDRV6LINKLOCAL); + assertTrue("v6only:global+dns+route", lp6.isProvisioned()); + + LinkProperties lp46 = new LinkProperties(); + lp46.addLinkAddress(LINKADDRV4); + lp46.addLinkAddress(LINKADDRV6); + lp46.addDnsServer(DNS1); + lp46.addDnsServer(DNS6); + assertFalse("dualstack:missing-routes", lp46.isProvisioned()); + lp46.addRoute(new RouteInfo(GATEWAY1)); + assertTrue("dualstack:v4-provisioned", lp46.isProvisioned()); + lp6.addRoute(new RouteInfo(GATEWAY6)); + assertTrue("dualstack:both-provisioned", lp46.isProvisioned()); + + // A link with an IPv6 address and default route, but IPv4 DNS server. + LinkProperties mixed = new LinkProperties(); + mixed.addLinkAddress(LINKADDRV6); + mixed.addDnsServer(DNS1); + mixed.addRoute(new RouteInfo(GATEWAY6)); + assertFalse("mixed:addr6+route6+dns4", mixed.isProvisioned()); + } } diff --git a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java index f916711..9bb44d0 100644 --- a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java +++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java @@ -59,7 +59,7 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { // Package 1 - Valid scorer. Pair<ResolveInfo, ResolveInfo> package1 = buildResolveInfo("package1", true, true, false); - // Package 2 - Receiver does not have BROADCAST_SCORE_NETWORKS permission. + // Package 2 - Receiver does not have BROADCAST_NETWORK_PRIVILEGED permission. Pair<ResolveInfo, ResolveInfo> package2 = buildResolveInfo("package2", false, true, false); // Package 3 - App does not have SCORE_NETWORKS permission. @@ -134,7 +134,7 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { resolveInfo.activityInfo.packageName = packageName; resolveInfo.activityInfo.applicationInfo = new ApplicationInfo(); if (hasReceiverPermission) { - resolveInfo.activityInfo.permission = permission.BROADCAST_SCORE_NETWORKS; + resolveInfo.activityInfo.permission = permission.BROADCAST_NETWORK_PRIVILEGED; } ResolveInfo configActivityInfo = null; diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java index b181122..9a08f41 100644 --- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java +++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java @@ -451,6 +451,40 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { assertIndexBeforeAfter(stats, 4, 4, Long.MAX_VALUE); } + public void testIntersects() throws Exception { + final long BUCKET_SIZE = HOUR_IN_MILLIS; + stats = new NetworkStatsHistory(BUCKET_SIZE); + + final long FIRST_START = TEST_START; + final long FIRST_END = FIRST_START + (2 * HOUR_IN_MILLIS); + final long SECOND_START = TEST_START + WEEK_IN_MILLIS; + final long SECOND_END = SECOND_START + HOUR_IN_MILLIS; + final long THIRD_START = TEST_START + (2 * WEEK_IN_MILLIS); + final long THIRD_END = THIRD_START + (2 * HOUR_IN_MILLIS); + + stats.recordData(FIRST_START, FIRST_END, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); + stats.recordData(SECOND_START, SECOND_END, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); + stats.recordData(THIRD_START, THIRD_END, + new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L)); + + assertFalse(stats.intersects(10, 20)); + assertFalse(stats.intersects(TEST_START + YEAR_IN_MILLIS, TEST_START + YEAR_IN_MILLIS + 1)); + assertFalse(stats.intersects(Long.MAX_VALUE, Long.MIN_VALUE)); + + assertTrue(stats.intersects(Long.MIN_VALUE, Long.MAX_VALUE)); + assertTrue(stats.intersects(10, TEST_START + YEAR_IN_MILLIS)); + assertTrue(stats.intersects(TEST_START, TEST_START)); + assertTrue(stats.intersects(TEST_START + DAY_IN_MILLIS, TEST_START + DAY_IN_MILLIS + 1)); + assertTrue(stats.intersects(TEST_START + DAY_IN_MILLIS, Long.MAX_VALUE)); + assertTrue(stats.intersects(TEST_START + 1, Long.MAX_VALUE)); + + assertFalse(stats.intersects(Long.MIN_VALUE, TEST_START - 1)); + assertTrue(stats.intersects(Long.MIN_VALUE, TEST_START)); + assertTrue(stats.intersects(Long.MIN_VALUE, TEST_START + 1)); + } + private static void assertIndexBeforeAfter( NetworkStatsHistory stats, int before, int after, long time) { assertEquals("unexpected before", before, stats.getIndexBefore(time)); diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java index 93e68eb..5c9e813 100644 --- a/core/tests/coretests/src/android/os/FileUtilsTest.java +++ b/core/tests/coretests/src/android/os/FileUtilsTest.java @@ -180,6 +180,51 @@ public class FileUtilsTest extends AndroidTestCase { assertDirContents("file1", "file2"); } + public void testValidExtFilename() throws Exception { + assertTrue(FileUtils.isValidExtFilename("a")); + assertTrue(FileUtils.isValidExtFilename("foo.bar")); + assertTrue(FileUtils.isValidExtFilename("foo bar.baz")); + assertTrue(FileUtils.isValidExtFilename("foo.bar.baz")); + assertTrue(FileUtils.isValidExtFilename(".bar")); + assertTrue(FileUtils.isValidExtFilename("foo~!@#$%^&*()_[]{}+bar")); + + assertFalse(FileUtils.isValidExtFilename(null)); + assertFalse(FileUtils.isValidExtFilename(".")); + assertFalse(FileUtils.isValidExtFilename("../foo")); + assertFalse(FileUtils.isValidExtFilename("/foo")); + + assertEquals(".._foo", FileUtils.buildValidExtFilename("../foo")); + assertEquals("_foo", FileUtils.buildValidExtFilename("/foo")); + assertEquals("foo_bar", FileUtils.buildValidExtFilename("foo\0bar")); + assertEquals(".foo", FileUtils.buildValidExtFilename(".foo")); + assertEquals("foo.bar", FileUtils.buildValidExtFilename("foo.bar")); + } + + public void testValidFatFilename() throws Exception { + assertTrue(FileUtils.isValidFatFilename("a")); + assertTrue(FileUtils.isValidFatFilename("foo bar.baz")); + assertTrue(FileUtils.isValidFatFilename("foo.bar.baz")); + assertTrue(FileUtils.isValidFatFilename(".bar")); + assertTrue(FileUtils.isValidFatFilename("foo.bar")); + assertTrue(FileUtils.isValidFatFilename("foo bar")); + assertTrue(FileUtils.isValidFatFilename("foo+bar")); + assertTrue(FileUtils.isValidFatFilename("foo,bar")); + + assertFalse(FileUtils.isValidFatFilename("foo*bar")); + assertFalse(FileUtils.isValidFatFilename("foo?bar")); + assertFalse(FileUtils.isValidFatFilename("foo<bar")); + assertFalse(FileUtils.isValidFatFilename(null)); + assertFalse(FileUtils.isValidFatFilename(".")); + assertFalse(FileUtils.isValidFatFilename("../foo")); + assertFalse(FileUtils.isValidFatFilename("/foo")); + + assertEquals(".._foo", FileUtils.buildValidFatFilename("../foo")); + assertEquals("_foo", FileUtils.buildValidFatFilename("/foo")); + assertEquals(".foo", FileUtils.buildValidFatFilename(".foo")); + assertEquals("foo.bar", FileUtils.buildValidFatFilename("foo.bar")); + assertEquals("foo_bar__baz", FileUtils.buildValidFatFilename("foo?bar**baz")); + } + private void touch(String name, long age) throws Exception { final File file = new File(mDir, name); file.createNewFile(); diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodTest.java index 577dd64..1557918 100644 --- a/core/tests/inputmethodtests/src/android/os/InputMethodTest.java +++ b/core/tests/inputmethodtests/src/android/os/InputMethodTest.java @@ -33,6 +33,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Objects; public class InputMethodTest extends InstrumentationTestCase { private static final boolean IS_AUX = true; @@ -46,84 +47,104 @@ public class InputMethodTest extends InstrumentationTestCase { private static final Locale LOCALE_EN_IN = new Locale("en", "IN"); private static final Locale LOCALE_HI = new Locale("hi"); private static final Locale LOCALE_JA_JP = new Locale("ja", "JP"); + private static final Locale LOCALE_ZH_CN = new Locale("zh", "CN"); + private static final Locale LOCALE_ZH_TW = new Locale("zh", "TW"); private static final String SUBTYPE_MODE_KEYBOARD = "keyboard"; private static final String SUBTYPE_MODE_VOICE = "voice"; @SmallTest public void testVoiceImes() throws Exception { // locale: en_US - assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US, !IS_SYSTEM_READY, - "DummyDefaultEnKeyboardIme"); - assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US, !IS_SYSTEM_READY, - "DummyDefaultEnKeyboardIme"); - assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US, IS_SYSTEM_READY, - "DummyDefaultAutoVoiceIme", "DummyDefaultEnKeyboardIme"); - assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US, IS_SYSTEM_READY, - "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1", - "DummyDefaultEnKeyboardIme"); + assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US, + !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); + assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US, + !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme"); + assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US, + IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); + assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US, + IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0", + "DummyNonDefaultAutoVoiceIme1"); // locale: en_GB - assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB, !IS_SYSTEM_READY, - "DummyDefaultEnKeyboardIme"); - assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB, !IS_SYSTEM_READY, - "DummyDefaultEnKeyboardIme"); - assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB, IS_SYSTEM_READY, - "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); - assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB, IS_SYSTEM_READY, - "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1", - "DummyDefaultEnKeyboardIme"); + assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB, + !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); + assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB, + !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme"); + assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB, + IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); + assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB, + IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0", + "DummyNonDefaultAutoVoiceIme1"); // locale: ja_JP - assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP, !IS_SYSTEM_READY, - "DummyDefaultEnKeyboardIme"); - assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, !IS_SYSTEM_READY, - "DummyDefaultEnKeyboardIme"); - assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP, IS_SYSTEM_READY, - "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); - assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, IS_SYSTEM_READY, - "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1", - "DummyDefaultEnKeyboardIme"); + assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP, + !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); + assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, + !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme"); + assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP, + IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); + assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, + IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0", + "DummyNonDefaultAutoVoiceIme1"); } @SmallTest public void testKeyboardImes() throws Exception { // locale: en_US - assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_US, !IS_SYSTEM_READY, - "com.android.apps.inputmethod.latin"); - assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_US, IS_SYSTEM_READY, - "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin"); + assertDefaultEnabledImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US, + !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin"); + assertDefaultEnabledImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US, + IS_SYSTEM_READY, "com.android.apps.inputmethod.latin", + "com.android.apps.inputmethod.voice"); // locale: en_GB - assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_GB, !IS_SYSTEM_READY, - "com.android.apps.inputmethod.latin"); - assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_GB, IS_SYSTEM_READY, - "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin"); + assertDefaultEnabledImes(getSamplePreinstalledImes("en-rGB"), LOCALE_EN_GB, + !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin"); + assertDefaultEnabledImes(getSamplePreinstalledImes("en-rGB"), LOCALE_EN_GB, + IS_SYSTEM_READY, "com.android.apps.inputmethod.latin", + "com.android.apps.inputmethod.voice"); // locale: en_IN - assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_IN, !IS_SYSTEM_READY, - "com.android.apps.inputmethod.latin"); - assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_IN, IS_SYSTEM_READY, - "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.hindi"); + assertDefaultEnabledImes(getSamplePreinstalledImes("en-rIN"), LOCALE_EN_IN, + !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin"); + assertDefaultEnabledImes(getSamplePreinstalledImes("en-rIN"), LOCALE_EN_IN, + IS_SYSTEM_READY, "com.android.apps.inputmethod.hindi", + "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice"); // locale: hi - assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_HI, !IS_SYSTEM_READY, - "com.android.apps.inputmethod.latin"); - assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_HI, IS_SYSTEM_READY, - "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.hindi"); + assertDefaultEnabledImes(getSamplePreinstalledImes("hi"), LOCALE_HI, + !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin"); + assertDefaultEnabledImes(getSamplePreinstalledImes("hi"), LOCALE_HI, + IS_SYSTEM_READY, "com.android.apps.inputmethod.hindi", + "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice"); // locale: ja_JP - assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_JA_JP, !IS_SYSTEM_READY, - "com.android.apps.inputmethod.latin"); - assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_JA_JP, IS_SYSTEM_READY, - "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.japanese"); + assertDefaultEnabledImes(getSamplePreinstalledImes("ja-rJP"), LOCALE_JA_JP, + !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin"); + assertDefaultEnabledImes(getSamplePreinstalledImes("ja-rJP"), LOCALE_JA_JP, + IS_SYSTEM_READY, "com.android.apps.inputmethod.japanese", + "com.android.apps.inputmethod.voice"); + + // locale: zh_CN + assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rCN"), LOCALE_ZH_CN, + !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin"); + assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rCN"), LOCALE_ZH_CN, + IS_SYSTEM_READY, "com.android.apps.inputmethod.pinyin", + "com.android.apps.inputmethod.voice"); + + // locale: zh_TW + // Note: In this case, no IME is suitable for the system locale. Hence we will pick up a + // fallback IME regardless of the "default" attribute. + assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rTW"), LOCALE_ZH_TW, + !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin"); + assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rTW"), LOCALE_ZH_TW, + IS_SYSTEM_READY, "com.android.apps.inputmethod.latin", + "com.android.apps.inputmethod.voice"); } @SmallTest public void testParcelable() throws Exception { - final ArrayList<InputMethodInfo> originalList = getSamplePreinstalledImes(); + final ArrayList<InputMethodInfo> originalList = getSamplePreinstalledImes("en-rUS"); final List<InputMethodInfo> clonedList = cloneViaParcel(originalList); assertNotNull(clonedList); final List<InputMethodInfo> clonedClonedList = cloneViaParcel(clonedList); @@ -139,11 +160,14 @@ public class InputMethodTest extends InstrumentationTestCase { } private void assertDefaultEnabledImes(final ArrayList<InputMethodInfo> preinstalledImes, - final Locale systemLocale, final boolean isSystemReady, String... imeNames) { + final Locale systemLocale, final boolean isSystemReady, String... expectedImeNames) { final Context context = getInstrumentation().getTargetContext(); - assertEquals(new HashSet<String>(Arrays.asList(imeNames)), - getPackageNames(callGetDefaultEnabledImesUnderWithLocale(context, - isSystemReady, preinstalledImes, systemLocale))); + final String[] actualImeNames = getPackageNames(callGetDefaultEnabledImesUnderWithLocale( + context, isSystemReady, preinstalledImes, systemLocale)); + assertEquals(expectedImeNames.length, actualImeNames.length); + for (int i = 0; i < expectedImeNames.length; ++i) { + assertEquals(expectedImeNames[i], actualImeNames[i]); + } } private static List<InputMethodInfo> cloneViaParcel(final List<InputMethodInfo> list) { @@ -172,11 +196,10 @@ public class InputMethodTest extends InstrumentationTestCase { } } - private HashSet<String> getPackageNames(final ArrayList<InputMethodInfo> imis) { - final HashSet<String> packageNames = new HashSet<>(); - for (final InputMethodInfo imi : imis) { - final String actualPackageName = imi.getPackageName(); - packageNames.add(actualPackageName); + private String[] getPackageNames(final ArrayList<InputMethodInfo> imis) { + final String[] packageNames = new String[imis.size()]; + for (int i = 0; i < imis.size(); ++i) { + packageNames[i] = imis.get(i).getPackageName(); } return packageNames; } @@ -278,21 +301,34 @@ public class InputMethodTest extends InstrumentationTestCase { return preinstalledImes; } - private static ArrayList<InputMethodInfo> getSamplePreinstalledImes() { + private static boolean contains(final String[] textList, final String textToBeChecked) { + if (textList == null) { + return false; + } + for (final String text : textList) { + if (Objects.equals(textToBeChecked, text)) { + return true; + } + } + return false; + } + + private static ArrayList<InputMethodInfo> getSamplePreinstalledImes(final String localeString) { ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>(); // a dummy Voice IME { + final boolean isDefaultIme = false; final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); subtypes.add(createDummyInputMethodSubtype("", SUBTYPE_MODE_VOICE, IS_AUX, IS_AUTO, !IS_ASCII_CAPABLE)); preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.voice", - "com.android.inputmethod.voice", "DummyVoiceIme", IS_AUX, IS_DEFAULT, + "com.android.inputmethod.voice", "DummyVoiceIme", IS_AUX, isDefaultIme, subtypes)); } - // a dummy Hindi IME { + final boolean isDefaultIme = contains(new String[]{ "hi", "en-rIN" }, localeString); final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); // TODO: This subtype should be marked as IS_ASCII_CAPABLE subtypes.add(createDummyInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, @@ -300,32 +336,36 @@ public class InputMethodTest extends InstrumentationTestCase { subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_AUTO, !IS_ASCII_CAPABLE)); preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.hindi", - "com.android.inputmethod.hindi", "DummyHindiIme", !IS_AUX, IS_DEFAULT, + "com.android.inputmethod.hindi", "DummyHindiIme", !IS_AUX, isDefaultIme, subtypes)); } // a dummy Pinyin IME { + final boolean isDefaultIme = contains(new String[]{ "zh-rCN" }, localeString); final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); subtypes.add(createDummyInputMethodSubtype("zh_CN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_AUTO, !IS_ASCII_CAPABLE)); - preinstalledImes.add(createDummyInputMethodInfo("ccom.android.apps.inputmethod.pinyin", - "com.android.apps.inputmethod.pinyin", "DummyPinyinIme", !IS_AUX, IS_DEFAULT, + preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.pinyin", + "com.android.apps.inputmethod.pinyin", "DummyPinyinIme", !IS_AUX, isDefaultIme, subtypes)); } - // a dummy Korian IME + // a dummy Korean IME { + final boolean isDefaultIme = contains(new String[]{ "ko" }, localeString); final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); subtypes.add(createDummyInputMethodSubtype("ko", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_AUTO, !IS_ASCII_CAPABLE)); preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.korean", - "com.android.apps.inputmethod.korean", "DummyKorianIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.korean", "DummyKoreanIme", !IS_AUX, isDefaultIme, subtypes)); } // a dummy Latin IME { + final boolean isDefaultIme = contains( + new String[]{ "en-rUS", "en-rGB", "en-rIN", "en", "hi" }, localeString); final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_AUTO, IS_ASCII_CAPABLE)); @@ -336,12 +376,13 @@ public class InputMethodTest extends InstrumentationTestCase { subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_AUTO, IS_ASCII_CAPABLE)); preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.latin", - "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, + "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, isDefaultIme, subtypes)); } // a dummy Japanese IME { + final boolean isDefaultIme = contains(new String[]{ "ja", "ja-rJP" }, localeString); final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); subtypes.add(createDummyInputMethodSubtype("ja", SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_AUTO, !IS_ASCII_CAPABLE)); @@ -349,7 +390,7 @@ public class InputMethodTest extends InstrumentationTestCase { !IS_AUTO, !IS_ASCII_CAPABLE)); preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.japanese", "com.android.apps.inputmethod.japanese", "DummyJapaneseIme", !IS_AUX, - IS_DEFAULT, subtypes)); + isDefaultIme, subtypes)); } return preinstalledImes; |