diff options
27 files changed, 734 insertions, 240 deletions
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 32bf66a92fd5..2236291fd248 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -24,7 +24,10 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import android.annotation.IntRange; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.app.ActivityManager; import android.content.ComponentName; import android.content.Intent; @@ -58,7 +61,6 @@ import android.util.jar.StrictJarFile; import android.view.Gravity; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -85,7 +87,6 @@ import java.util.zip.ZipEntry; import libcore.io.IoUtils; import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; -import static android.content.pm.ActivityInfo.FLAG_IMMERSIVE; import static android.content.pm.ActivityInfo.FLAG_ON_TOP_LAUNCHER; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; @@ -2103,63 +2104,22 @@ public class PackageParser { sa.recycle(); - if (minCode != null) { - boolean allowedCodename = false; - for (String codename : SDK_CODENAMES) { - if (minCode.equals(codename)) { - allowedCodename = true; - break; - } - } - if (!allowedCodename) { - if (SDK_CODENAMES.length > 0) { - outError[0] = "Requires development platform " + minCode - + " (current platform is any of " - + Arrays.toString(SDK_CODENAMES) + ")"; - } else { - outError[0] = "Requires development platform " + minCode - + " but this is a release platform."; - } - mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; - return null; - } - pkg.applicationInfo.minSdkVersion = - android.os.Build.VERSION_CODES.CUR_DEVELOPMENT; - } else if (minVers > SDK_VERSION) { - outError[0] = "Requires newer sdk version #" + minVers - + " (current version is #" + SDK_VERSION + ")"; + final int minSdkVersion = PackageParser.computeMinSdkVersion(minVers, minCode, + SDK_VERSION, SDK_CODENAMES, outError); + if (minSdkVersion < 0) { mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; return null; - } else { - pkg.applicationInfo.minSdkVersion = minVers; } - if (targetCode != null) { - boolean allowedCodename = false; - for (String codename : SDK_CODENAMES) { - if (targetCode.equals(codename)) { - allowedCodename = true; - break; - } - } - if (!allowedCodename) { - if (SDK_CODENAMES.length > 0) { - outError[0] = "Requires development platform " + targetCode - + " (current platform is any of " - + Arrays.toString(SDK_CODENAMES) + ")"; - } else { - outError[0] = "Requires development platform " + targetCode - + " but this is a release platform."; - } - mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; - return null; - } - // If the code matches, it definitely targets this SDK. - pkg.applicationInfo.targetSdkVersion - = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT; - } else { - pkg.applicationInfo.targetSdkVersion = targetVers; + final int targetSdkVersion = PackageParser.computeTargetSdkVersion(targetVers, + targetCode, SDK_VERSION, SDK_CODENAMES, outError); + if (targetSdkVersion < 0) { + mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; + return null; } + + pkg.applicationInfo.minSdkVersion = minSdkVersion; + pkg.applicationInfo.targetSdkVersion = targetSdkVersion; } XmlUtils.skipCurrentTag(parser); @@ -2407,6 +2367,137 @@ public class PackageParser { return pkg; } + /** + * Computes the targetSdkVersion to use at runtime. If the package is not + * compatible with this platform, populates {@code outError[0]} with an + * error message. + * <p> + * If {@code targetCode} is not specified, e.g. the value is {@code null}, + * then the {@code targetVers} will be returned unmodified. + * <p> + * Otherwise, the behavior varies based on whether the current platform + * is a pre-release version, e.g. the {@code platformSdkCodenames} array + * has length > 0: + * <ul> + * <li>If this is a pre-release platform and the value specified by + * {@code targetCode} is contained within the array of allowed pre-release + * codenames, this method will return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}. + * <li>If this is a released platform, this method will return -1 to + * indicate that the package is not compatible with this platform. + * </ul> + * + * @param targetVers targetSdkVersion number, if specified in the + * application manifest, or 0 otherwise + * @param targetCode targetSdkVersion code, if specified in the application + * manifest, or {@code null} otherwise + * @param platformSdkVersion platform SDK version number, typically + * Build.VERSION.SDK_INT + * @param platformSdkCodenames array of allowed pre-release SDK codenames + * for this platform + * @param outError output array to populate with error, if applicable + * @return the targetSdkVersion to use at runtime, or -1 if the package is + * not compatible with this platform + * @hide Exposed for unit testing only. + */ + @TestApi + public static int computeTargetSdkVersion(@IntRange(from = 0) int targetVers, + @Nullable String targetCode, @IntRange(from = 1) int platformSdkVersion, + @NonNull String[] platformSdkCodenames, @NonNull String[] outError) { + // If it's a release SDK, return the version number unmodified. + if (targetCode == null) { + return targetVers; + } + + // If it's a pre-release SDK and the codename matches this platform, it + // definitely targets this SDK. + if (ArrayUtils.contains(platformSdkCodenames, targetCode)) { + return Build.VERSION_CODES.CUR_DEVELOPMENT; + } + + // Otherwise, we're looking at an incompatible pre-release SDK. + if (platformSdkCodenames.length > 0) { + outError[0] = "Requires development platform " + targetCode + + " (current platform is any of " + + Arrays.toString(platformSdkCodenames) + ")"; + } else { + outError[0] = "Requires development platform " + targetCode + + " but this is a release platform."; + } + return -1; + } + + /** + * Computes the minSdkVersion to use at runtime. If the package is not + * compatible with this platform, populates {@code outError[0]} with an + * error message. + * <p> + * If {@code minCode} is not specified, e.g. the value is {@code null}, + * then behavior varies based on the {@code platformSdkVersion}: + * <ul> + * <li>If the platform SDK version is greater than or equal to the + * {@code minVers}, returns the {@code mniVers} unmodified. + * <li>Otherwise, returns -1 to indicate that the package is not + * compatible with this platform. + * </ul> + * <p> + * Otherwise, the behavior varies based on whether the current platform + * is a pre-release version, e.g. the {@code platformSdkCodenames} array + * has length > 0: + * <ul> + * <li>If this is a pre-release platform and the value specified by + * {@code targetCode} is contained within the array of allowed pre-release + * codenames, this method will return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}. + * <li>If this is a released platform, this method will return -1 to + * indicate that the package is not compatible with this platform. + * </ul> + * + * @param minVers minSdkVersion number, if specified in the application + * manifest, or 1 otherwise + * @param minCode minSdkVersion code, if specified in the application + * manifest, or {@code null} otherwise + * @param platformSdkVersion platform SDK version number, typically + * Build.VERSION.SDK_INT + * @param platformSdkCodenames array of allowed prerelease SDK codenames + * for this platform + * @param outError output array to populate with error, if applicable + * @return the minSdkVersion to use at runtime, or -1 if the package is not + * compatible with this platform + * @hide Exposed for unit testing only. + */ + @TestApi + public static int computeMinSdkVersion(@IntRange(from = 1) int minVers, + @Nullable String minCode, @IntRange(from = 1) int platformSdkVersion, + @NonNull String[] platformSdkCodenames, @NonNull String[] outError) { + // If it's a release SDK, make sure we meet the minimum SDK requirement. + if (minCode == null) { + if (minVers <= platformSdkVersion) { + return minVers; + } + + // We don't meet the minimum SDK requirement. + outError[0] = "Requires newer sdk version #" + minVers + + " (current version is #" + platformSdkVersion + ")"; + return -1; + } + + // If it's a pre-release SDK and the codename matches this platform, we + // definitely meet the minimum SDK requirement. + if (ArrayUtils.contains(platformSdkCodenames, minCode)) { + return Build.VERSION_CODES.CUR_DEVELOPMENT; + } + + // Otherwise, we're looking at an incompatible pre-release SDK. + if (platformSdkCodenames.length > 0) { + outError[0] = "Requires development platform " + minCode + + " (current platform is any of " + + Arrays.toString(platformSdkCodenames) + ")"; + } else { + outError[0] = "Requires development platform " + minCode + + " but this is a release platform."; + } + return -1; + } + private FeatureInfo parseUsesFeature(Resources res, AttributeSet attrs) { FeatureInfo fi = new FeatureInfo(); TypedArray sa = res.obtainAttributes(attrs, diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java index 6196a97024e6..99f6c2a9c58d 100644 --- a/core/java/android/util/EventLog.java +++ b/core/java/android/util/EventLog.java @@ -56,6 +56,7 @@ public class EventLog { /** A previously logged event read from the logs. Instances are thread safe. */ public static final class Event { private final ByteBuffer mBuffer; + private Exception mLastWtf; // Layout of event log entry received from Android logger. // see system/core/include/log/logger.h @@ -116,13 +117,19 @@ public class EventLog { offset = V1_PAYLOAD_START; } mBuffer.limit(offset + mBuffer.getShort(LENGTH_OFFSET)); + if ((offset + DATA_OFFSET) >= mBuffer.limit()) { + // no payload + return null; + } mBuffer.position(offset + DATA_OFFSET); // Just after the tag. return decodeObject(); } catch (IllegalArgumentException e) { Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e); + mLastWtf = e; return null; } catch (BufferUnderflowException e) { Log.wtf(TAG, "Truncated entry payload: tag=" + getTag(), e); + mLastWtf = e; return null; } } @@ -148,6 +155,7 @@ public class EventLog { return new String(mBuffer.array(), start, length, "UTF-8"); } catch (UnsupportedEncodingException e) { Log.wtf(TAG, "UTF-8 is not supported", e); + mLastWtf = e; return null; } @@ -173,6 +181,24 @@ public class EventLog { byte[] bytes = mBuffer.array(); return Arrays.copyOf(bytes, bytes.length); } + + /** + * Retreive the last WTF error generated by this object. + * @hide + */ + //VisibleForTesting + public Exception getLastError() { + return mLastWtf; + } + + /** + * Clear the error state for this object. + * @hide + */ + //VisibleForTesting + public void clearError() { + mLastWtf = null; + } } // We assume that the native methods deal with any concurrency issues. diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 69c705494932..724fccc2e91b 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -395,7 +395,7 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding SkAlphaType alphaType = codec->computeOutputAlphaType(requireUnpremultiplied); const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(), - decodeColorType, alphaType, codec->computeOutputColorSpace(decodeColorType)); + decodeColorType, alphaType); // We always decode to sRGB, but only mark the bitmap with a color space if linear // blending is enabled. diff --git a/core/tests/coretests/README b/core/tests/coretests/README new file mode 100644 index 000000000000..4a6984320e61 --- /dev/null +++ b/core/tests/coretests/README @@ -0,0 +1,50 @@ +* Copyright (C) 2016 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. + + +INTRODUCTION + +The Android platform core tests (APCT) consist of unit tests for core platform +functionality. These differ from CTS in that they are not necessarily testing +public APIs and are not guaranteed to work outside of AOSP builds. + + +INSTRUCTIONS + +To run a test or set of tests, first build the FrameworksCoreTests package: + + make FrameworksCoreTests + +Next, install the resulting APK and run tests as you would normal JUnit tests: + + adb install out/target/product/.../data/app/FrameworksCoreTests/FrameworksCoreTests.apk + adb shell am instrument -w \ + com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner + +To run a tests within a specific package, add the following argument AFTER -w: + + -e package android.content.pm + +To run a specific test or method within a test: + + -e class android.content.pm.PackageParserTest + -e class android.content.pm.PackageParserTest#testComputeMinSdkVersion + +To run tests in debug mode: + + -e debug true + +For more arguments, see the guide to command=line testing: + + https://developer.android.com/studio/test/command-line.html diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java new file mode 100644 index 000000000000..2a3c22c64ec2 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import android.os.Build; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class PackageParserTest { + private static final String RELEASED = null; + private static final String OLDER_PRE_RELEASE = "A"; + private static final String PRE_RELEASE = "B"; + private static final String NEWER_PRE_RELEASE = "C"; + + private static final String[] CODENAMES_RELEASED = { /* empty */ }; + private static final String[] CODENAMES_PRE_RELEASE = { PRE_RELEASE }; + + private static final int OLDER_VERSION = 10; + private static final int PLATFORM_VERSION = 20; + private static final int NEWER_VERSION = 30; + + private void verifyComputeMinSdkVersion(int minSdkVersion, String minSdkCodename, + boolean isPlatformReleased, int expectedMinSdk) { + final String[] outError = new String[1]; + final int result = PackageParser.computeMinSdkVersion( + minSdkVersion, + minSdkCodename, + PLATFORM_VERSION, + isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE, + outError); + + assertEquals(result, expectedMinSdk); + + if (expectedMinSdk == -1) { + assertNotNull(outError[0]); + } else { + assertNull(outError[0]); + } + } + + @Test + public void testComputeMinSdkVersion_preReleasePlatform() { + // Do allow older release minSdkVersion on pre-release platform. + // APP: Released API 10 + // DEV: Pre-release API 20 + verifyComputeMinSdkVersion(OLDER_VERSION, RELEASED, false, OLDER_VERSION); + + // Do allow same release minSdkVersion on pre-release platform. + // APP: Released API 20 + // DEV: Pre-release API 20 + verifyComputeMinSdkVersion(PLATFORM_VERSION, RELEASED, false, PLATFORM_VERSION); + + // Don't allow newer release minSdkVersion on pre-release platform. + // APP: Released API 30 + // DEV: Pre-release API 20 + verifyComputeMinSdkVersion(NEWER_VERSION, RELEASED, false, -1); + + // Don't allow older pre-release minSdkVersion on pre-release platform. + // APP: Pre-release API 10 + // DEV: Pre-release API 20 + verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1); + + // Do allow same pre-release minSdkVersion on pre-release platform, + // but overwrite the specified version with CUR_DEVELOPMENT. + // APP: Pre-release API 20 + // DEV: Pre-release API 20 + verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false, + Build.VERSION_CODES.CUR_DEVELOPMENT); + + // Don't allow newer pre-release minSdkVersion on pre-release platform. + // APP: Pre-release API 30 + // DEV: Pre-release API 20 + verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1); + } + + @Test + public void testComputeMinSdkVersion_releasedPlatform() { + // Do allow older release minSdkVersion on released platform. + // APP: Released API 10 + // DEV: Released API 20 + verifyComputeMinSdkVersion(OLDER_VERSION, RELEASED, true, OLDER_VERSION); + + // Do allow same release minSdkVersion on released platform. + // APP: Released API 20 + // DEV: Released API 20 + verifyComputeMinSdkVersion(PLATFORM_VERSION, RELEASED, true, PLATFORM_VERSION); + + // Don't allow newer release minSdkVersion on released platform. + // APP: Released API 30 + // DEV: Released API 20 + verifyComputeMinSdkVersion(NEWER_VERSION, RELEASED, true, -1); + + // Don't allow older pre-release minSdkVersion on released platform. + // APP: Pre-release API 10 + // DEV: Released API 20 + verifyComputeMinSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1); + + // Don't allow same pre-release minSdkVersion on released platform. + // APP: Pre-release API 20 + // DEV: Released API 20 + verifyComputeMinSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1); + + // Don't allow newer pre-release minSdkVersion on released platform. + // APP: Pre-release API 30 + // DEV: Released API 20 + verifyComputeMinSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1); + } + + private void verifyComputeTargetSdkVersion(int targetSdkVersion, String targetSdkCodename, + boolean isPlatformReleased, int expectedTargetSdk) { + final String[] outError = new String[1]; + final int result = PackageParser.computeTargetSdkVersion( + targetSdkVersion, + targetSdkCodename, + PLATFORM_VERSION, + isPlatformReleased ? CODENAMES_RELEASED : CODENAMES_PRE_RELEASE, + outError); + + assertEquals(result, expectedTargetSdk); + + if (expectedTargetSdk == -1) { + assertNotNull(outError[0]); + } else { + assertNull(outError[0]); + } + } + + @Test + public void testComputeTargetSdkVersion_preReleasePlatform() { + // Do allow older release targetSdkVersion on pre-release platform. + // APP: Released API 10 + // DEV: Pre-release API 20 + verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, false, OLDER_VERSION); + + // Do allow same release targetSdkVersion on pre-release platform. + // APP: Released API 20 + // DEV: Pre-release API 20 + verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, false, PLATFORM_VERSION); + + // Do allow newer release targetSdkVersion on pre-release platform. + // APP: Released API 30 + // DEV: Pre-release API 20 + verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, false, NEWER_VERSION); + + // Don't allow older pre-release targetSdkVersion on pre-release platform. + // APP: Pre-release API 10 + // DEV: Pre-release API 20 + verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, false, -1); + + // Do allow same pre-release targetSdkVersion on pre-release platform, + // but overwrite the specified version with CUR_DEVELOPMENT. + // APP: Pre-release API 20 + // DEV: Pre-release API 20 + verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, false, + Build.VERSION_CODES.CUR_DEVELOPMENT); + + // Don't allow newer pre-release targetSdkVersion on pre-release platform. + // APP: Pre-release API 30 + // DEV: Pre-release API 20 + verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, false, -1); + } + + @Test + public void testComputeTargetSdkVersion_releasedPlatform() { + // Do allow older release targetSdkVersion on released platform. + // APP: Released API 10 + // DEV: Released API 20 + verifyComputeTargetSdkVersion(OLDER_VERSION, RELEASED, true, OLDER_VERSION); + + // Do allow same release targetSdkVersion on released platform. + // APP: Released API 20 + // DEV: Released API 20 + verifyComputeTargetSdkVersion(PLATFORM_VERSION, RELEASED, true, PLATFORM_VERSION); + + // Do allow newer release targetSdkVersion on released platform. + // APP: Released API 30 + // DEV: Released API 20 + verifyComputeTargetSdkVersion(NEWER_VERSION, RELEASED, true, NEWER_VERSION); + + // Don't allow older pre-release targetSdkVersion on released platform. + // APP: Pre-release API 10 + // DEV: Released API 20 + verifyComputeTargetSdkVersion(OLDER_VERSION, OLDER_PRE_RELEASE, true, -1); + + // Don't allow same pre-release targetSdkVersion on released platform. + // APP: Pre-release API 20 + // DEV: Released API 20 + verifyComputeTargetSdkVersion(PLATFORM_VERSION, PRE_RELEASE, true, -1); + + // Don't allow newer pre-release targetSdkVersion on released platform. + // APP: Pre-release API 30 + // DEV: Released API 20 + verifyComputeTargetSdkVersion(NEWER_VERSION, NEWER_PRE_RELEASE, true, -1); + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java index f3658c38ca35..6ccba92e2e5f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java @@ -50,19 +50,25 @@ public class CategoryManager { private final Map<String, DashboardCategory> mCategoryByKeyMap; private List<DashboardCategory> mCategories; + private String mExtraAction; public static CategoryManager get(Context context) { + return get(context, null); + } + + public static CategoryManager get(Context context, String action) { if (sInstance == null) { - sInstance = new CategoryManager(context); + sInstance = new CategoryManager(context, action); } return sInstance; } - CategoryManager(Context context) { + CategoryManager(Context context, String action) { mTileByComponentCache = new ArrayMap<>(); mCategoryByKeyMap = new ArrayMap<>(); mInterestingConfigChanges = new InterestingConfigChanges(); mInterestingConfigChanges.applyNewConfig(context.getResources()); + mExtraAction = action; } public synchronized DashboardCategory getTilesByCategory(Context context, String categoryKey) { @@ -111,7 +117,7 @@ public class CategoryManager { } mCategoryByKeyMap.clear(); mCategories = TileUtils.getCategories(context, mTileByComponentCache, - false /* categoryDefinedInManifest */); + false /* categoryDefinedInManifest */, mExtraAction); for (DashboardCategory category : mCategories) { mCategoryByKeyMap.put(category.key, category); } diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java index d12e8c09b8c0..6c5a09d23c61 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java @@ -144,6 +144,19 @@ public class TileUtils { */ public static List<DashboardCategory> getCategories(Context context, Map<Pair<String, String>, Tile> cache, boolean categoryDefinedInManifest) { + return getCategories(context, cache, categoryDefinedInManifest, null); + } + + /** + * Build a list of DashboardCategory. + * @param categoryDefinedInManifest If true, an dummy activity must exists in manifest to + * represent this category (eg: .Settings$DeviceSettings) + * @param extraAction additional intent filter action to be used to build the dashboard + * categories + */ + public static List<DashboardCategory> getCategories(Context context, + Map<Pair<String, String>, Tile> cache, boolean categoryDefinedInManifest, + String extraAction) { final long startTime = System.currentTimeMillis(); boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) != 0; @@ -162,6 +175,9 @@ public class TileUtils { if (setup) { getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false); getTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false); + if (extraAction != null) { + getTilesForAction(context, user, extraAction, cache, null, tiles, false); + } } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java index 86b210ac4aef..c6c6aad22cf0 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java @@ -26,6 +26,8 @@ import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.os.Bundle; import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings.Global; import android.util.ArrayMap; import android.util.Pair; @@ -34,6 +36,7 @@ import com.android.settingslib.TestConfig; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; @@ -46,6 +49,7 @@ import java.util.Map; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.when; @@ -59,6 +63,8 @@ public class TileUtilsTest { private PackageManager mPackageManager; @Mock private Resources mResources; + @Mock + private UserManager mUserManager; @Before public void setUp() throws NameNotFoundException { @@ -127,6 +133,30 @@ public class TileUtilsTest { assertThat(outTiles.isEmpty()).isTrue(); } + @Test + public void getCategories_shouldHandleExtraIntentAction() { + final String testCategory = "category1"; + final String testAction = "action1"; + Map<Pair<String, String>, Tile> cache = new ArrayMap<>(); + List<ResolveInfo> info = new ArrayList<>(); + info.add(newInfo(true, testCategory)); + Global.putInt(mContext.getContentResolver(), Global.DEVICE_PROVISIONED, 1); + when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + List<UserHandle> userHandleList = new ArrayList<>(); + userHandleList.add(UserHandle.CURRENT); + when(mUserManager.getUserProfiles()).thenReturn(userHandleList); + + when(mPackageManager.queryIntentActivitiesAsUser(argThat(new ArgumentMatcher<Intent>() { + public boolean matches(Object event) { + return testAction.equals(((Intent) event).getAction()); + } + }), anyInt(), anyInt())).thenReturn(info); + + List<DashboardCategory> categoryList = TileUtils.getCategories( + mContext, cache, false /* categoryDefinedInManifest */, testAction); + + assertThat(categoryList.get(0).tiles.get(0).category).isEqualTo(testCategory); + } private ResolveInfo newInfo(boolean systemApp, String category) { return newInfo(systemApp, category, null); diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java index 9bbead46c33b..0be53b4b7c26 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java @@ -60,8 +60,6 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene private final QSDetailClipper mClipper; - private PhoneStatusBar mPhoneStatusBar; - private boolean isShown; private QSTileHost mHost; private RecyclerView mRecyclerView; @@ -119,7 +117,6 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene public void setHost(QSTileHost host) { mHost = host; - mPhoneStatusBar = host.getPhoneStatusBar(); mTileAdapter.setHost(host); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 6f00f521407c..137138127775 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -721,7 +721,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener if (task.isFreeformTask()) { mTmpTransform = stackLayout.getStackTransformScreenCoordinates(task, stackScroller.getStackScroll(), mTmpTransform, null, - windowOverrideRect); + windowOverrideRect, false /* useGridLayout */); Bitmap thumbnail = drawThumbnailTransitionBitmap(task, mTmpTransform, mThumbTransitionBitmapCache); Rect toTaskRect = new Rect(); @@ -771,7 +771,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener stackView.updateLayoutAlgorithm(true /* boundScroll */); stackView.updateToInitialState(); stackView.getStackAlgorithm().getStackTransformScreenCoordinates(launchTask, - stackView.getScroller().getStackScroll(), mTmpTransform, null, windowOverrideRect); + stackView.getScroller().getStackScroll(), mTmpTransform, null, windowOverrideRect, + Recents.getConfiguration().isGridEnabled); return mTmpTransform; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java index 3d528bed9fa9..052985647453 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java @@ -886,10 +886,10 @@ public class TaskStackLayoutAlgorithm { */ public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll, TaskViewTransform transformOut, TaskViewTransform frontTransform, - Rect windowOverrideRect) { + Rect windowOverrideRect, boolean useGridLayout) { TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform, true /* forceUpdate */, - false /* ignoreTaskOverrides */, false /* useGridLayout */); + false /* ignoreTaskOverrides */, useGridLayout); return transformToScreenCoordinates(transform, windowOverrideRect); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java index 71f559be6775..33fa3b0eb10a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java @@ -168,7 +168,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { /** Touch preprocessing for handling below */ public boolean onInterceptTouchEvent(MotionEvent ev) { // Pass through to swipe helper if we are swiping - mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev); + mInterceptedBySwipeHelper = isSwipingEnabled() && mSwipeHelper.onInterceptTouchEvent(ev); if (mInterceptedBySwipeHelper) { return true; } @@ -680,4 +680,11 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { public float getScaledDismissSize() { return 1.5f * Math.max(mSv.getWidth(), mSv.getHeight()); } + + /** + * Returns whether swiping is enabled. + */ + private boolean isSwipingEnabled() { + return !mSv.useGridLayout(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java index 325e71a847b0..630040098d10 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java @@ -42,7 +42,7 @@ public class GridTaskView extends TaskView { public GridTaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); mHeaderHeight = context.getResources().getDimensionPixelSize( - R.dimen.recents_task_view_header_height); + R.dimen.recents_grid_task_view_header_height); } @Override @@ -65,7 +65,7 @@ public class GridTaskView extends TaskView { protected void onConfigurationChanged() { super.onConfigurationChanged(); mHeaderHeight = mContext.getResources().getDimensionPixelSize( - R.dimen.recents_task_view_header_height); + R.dimen.recents_grid_task_view_header_height); mThumbnailView.setTranslationY(mHeaderHeight); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java index 567ab3b5966d..227ebdfacbda 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -165,10 +165,6 @@ public class QSTileHost implements QSTile.Host, Tunable { mHeader = view; } - public PhoneStatusBar getPhoneStatusBar() { - return mStatusBar; - } - public void destroy() { mHandlerThread.quitSafely(); mTiles.values().forEach(tile -> tile.destroy()); diff --git a/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java b/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java index f42092130af1..5cfe677efd57 100644 --- a/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java +++ b/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java @@ -81,11 +81,23 @@ public class LayoutInflaterBuilder { * @return Builder object post-modification. */ public LayoutInflaterBuilder replace(@NonNull Class from, @NonNull Class to) { + return replace(from.getName(), to); + } + + /** + * Instructs the Builder to configure the LayoutInflater such that all instances + * of one {@link View} will be replaced with instances of another during inflation. + * + * @param tag Instances of this tag will be replaced during inflation. + * @param to Instances of this class will be inflated as replacements. + * @return Builder object post-modification. + */ + public LayoutInflaterBuilder replace(@NonNull String tag, @NonNull Class to) { assertIfAlreadyBuilt(); if (mReplaceMap == null) { mReplaceMap = new ArrayMap<String, String>(); } - mReplaceMap.put(from.getName(), to.getName()); + mReplaceMap.put(tag, to.getName()); return this; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java index 391c45fb8487..8acd6ba5e1d7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java @@ -14,37 +14,31 @@ package com.android.systemui.qs; +import static junit.framework.Assert.assertEquals; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import android.content.Context; -import android.content.res.Resources; +import android.os.Handler; import android.os.Looper; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import android.text.SpannableStringBuilder; -import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; import android.widget.TextView; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.policy.SecurityController; +import com.android.systemui.util.LayoutInflaterBuilder; +import com.android.systemui.utils.TestableImageView; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import static junit.framework.Assert.assertEquals; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - @SmallTest @RunWith(AndroidJUnit4.class) public class QSFooterTest extends SysuiTestCase { @@ -53,28 +47,26 @@ public class QSFooterTest extends SysuiTestCase { private final String DEVICE_OWNER_PACKAGE = "TestDPC"; private final String VPN_PACKAGE = "TestVPN"; - private ViewGroup mRootView = mock(ViewGroup.class); - private TextView mFooterText = mock(TextView.class); - private ImageView mFooterIcon = mock(ImageView.class); - private ImageView mFooterIcon2 = mock(ImageView.class); + private ViewGroup mRootView; + private TextView mFooterText; + private TestableImageView mFooterIcon; + private TestableImageView mFooterIcon2; private QSFooter mFooter; - private Resources mResources; private SecurityController mSecurityController = mock(SecurityController.class); @Before public void setUp() { - when(mRootView.findViewById(R.id.footer_text)).thenReturn(mFooterText); - when(mRootView.findViewById(R.id.footer_icon)).thenReturn(mFooterIcon); - when(mRootView.findViewById(R.id.footer_icon2)).thenReturn(mFooterIcon2); - final LayoutInflater layoutInflater = mock(LayoutInflater.class); - when(layoutInflater.inflate(eq(R.layout.quick_settings_footer), anyObject(), anyBoolean())) - .thenReturn(mRootView); - final Context context = mock(Context.class); - when(context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).thenReturn(layoutInflater); - mResources = mContext.getResources(); - when(context.getResources()).thenReturn(mResources); - mFooter = new QSFooter(null, context); - reset(mRootView); + mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, + new LayoutInflaterBuilder(mContext) + .replace("ImageView", TestableImageView.class) + .build()); + Handler h = new Handler(Looper.getMainLooper()); + h.post(() -> mFooter = new QSFooter(null, mContext)); + waitForIdleSync(h); + mRootView = (ViewGroup) mFooter.getView(); + mFooterText = (TextView) mRootView.findViewById(R.id.footer_text); + mFooterIcon = (TestableImageView) mRootView.findViewById(R.id.footer_icon); + mFooterIcon2 = (TestableImageView) mRootView.findViewById(R.id.footer_icon2); mFooter.setHostEnvironment(null, mSecurityController, Looper.getMainLooper()); } @@ -86,8 +78,7 @@ public class QSFooterTest extends SysuiTestCase { mFooter.refreshState(); waitForIdleSync(mFooter.mHandler); - verify(mRootView).setVisibility(View.GONE); - verifyNoMoreInteractions(mRootView); + assertEquals(View.GONE, mRootView.getVisibility()); } @Test @@ -97,10 +88,8 @@ public class QSFooterTest extends SysuiTestCase { mFooter.refreshState(); waitForIdleSync(mFooter.mHandler); - verify(mFooterText).setText(mResources.getString(R.string.do_disclosure_generic)); - verifyNoMoreInteractions(mFooterText); - verify(mRootView).setVisibility(View.VISIBLE); - verifyNoMoreInteractions(mRootView); + assertEquals(mContext.getString(R.string.do_disclosure_generic), mFooterText.getText()); + assertEquals(View.VISIBLE, mRootView.getVisibility()); } @Test @@ -111,11 +100,9 @@ public class QSFooterTest extends SysuiTestCase { mFooter.refreshState(); waitForIdleSync(mFooter.mHandler); - verify(mFooterText).setText(mResources.getString(R.string.do_disclosure_with_name, - MANAGING_ORGANIZATION)); - verifyNoMoreInteractions(mFooterText); - verify(mRootView).setVisibility(View.VISIBLE); - verifyNoMoreInteractions(mRootView); + assertEquals(mContext.getString(R.string.do_disclosure_with_name, MANAGING_ORGANIZATION), + mFooterText.getText()); + assertEquals(View.VISIBLE, mRootView.getVisibility()); } @Test @@ -126,9 +113,9 @@ public class QSFooterTest extends SysuiTestCase { mFooter.refreshState(); waitForIdleSync(mFooter.mHandler); - verify(mFooterIcon).setVisibility(View.VISIBLE); - verify(mFooterIcon).setImageResource(R.drawable.ic_qs_network_logging); - verify(mFooterIcon2).setVisibility(View.INVISIBLE); + assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(R.drawable.ic_qs_network_logging, mFooterIcon.getLastImageResource()); + assertEquals(View.INVISIBLE, mFooterIcon2.getVisibility()); } @Test @@ -140,9 +127,10 @@ public class QSFooterTest extends SysuiTestCase { mFooter.refreshState(); waitForIdleSync(mFooter.mHandler); - verify(mFooterIcon).setVisibility(View.VISIBLE); - verify(mFooterIcon, never()).setImageResource(anyInt()); - verify(mFooterIcon2).setVisibility(View.INVISIBLE); + assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + // -1 == never set. + assertEquals(-1, mFooterIcon.getLastImageResource()); + assertEquals(View.INVISIBLE, mFooterIcon2.getVisibility()); } @Test @@ -154,10 +142,11 @@ public class QSFooterTest extends SysuiTestCase { mFooter.refreshState(); waitForIdleSync(mFooter.mHandler); - verify(mFooterIcon).setVisibility(View.VISIBLE); - verify(mFooterIcon, never()).setImageResource(anyInt()); - verify(mFooterIcon2).setVisibility(View.VISIBLE); - verify(mFooterIcon2, never()).setImageResource(anyInt()); + assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(View.VISIBLE, mFooterIcon2.getVisibility()); + // -1 == never set. + assertEquals(-1, mFooterIcon.getLastImageResource()); + assertEquals(-1, mFooterIcon2.getLastImageResource()); } @Test @@ -211,15 +200,15 @@ public class QSFooterTest extends SysuiTestCase { private CharSequence getExpectedMessage(boolean hasDeviceOwnerOrganization, boolean hasVPN) { final SpannableStringBuilder message = new SpannableStringBuilder(); message.append(hasDeviceOwnerOrganization ? - mResources.getString(R.string.monitoring_description_do_header_with_name, + mContext.getString(R.string.monitoring_description_do_header_with_name, MANAGING_ORGANIZATION, DEVICE_OWNER_PACKAGE) : - mResources.getString(R.string.monitoring_description_do_header_generic, + mContext.getString(R.string.monitoring_description_do_header_generic, DEVICE_OWNER_PACKAGE)); message.append("\n\n"); - message.append(mResources.getString(R.string.monitoring_description_do_body)); - message.append(mResources.getString( + message.append(mContext.getString(R.string.monitoring_description_do_body)); + message.append(mContext.getString( R.string.monitoring_description_do_learn_more_separator)); - message.append(mResources.getString(R.string.monitoring_description_do_learn_more), + message.append(mContext.getString(R.string.monitoring_description_do_learn_more), mFooter.new EnterprisePrivacySpan(), 0); return message; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index 350a95ff66f4..c0d5bbd35690 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -63,7 +63,7 @@ public class QSFragmentTest extends FragmentTestCase { when(userSwitcher.getKeyguardMonitor()).thenReturn(keyguardMonitor); when(userSwitcher.getUsers()).thenReturn(new ArrayList<>()); QSTileHost host = new QSTileHost(mContext, - mock(PhoneStatusBar.class), + null, getLeakChecker(BluetoothController.class), getLeakChecker(LocationController.class), getLeakChecker(RotationLockController.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java index 782a4890ba55..66ec7dd3f270 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java @@ -15,8 +15,10 @@ */ package com.android.systemui.qs.external; +import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyString; @@ -26,9 +28,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.ComponentName; -import android.content.Context; import android.content.Intent; -import android.content.ServiceConnection; import android.content.pm.PackageInfo; import android.content.pm.ServiceInfo; import android.net.Uri; @@ -50,15 +50,12 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; @SmallTest @RunWith(AndroidJUnit4.class) public class TileLifecycleManagerTest extends SysuiTestCase { private static final int TEST_FAIL_TIMEOUT = 5000; - private final Context mMockContext = Mockito.mock(Context.class); private final PackageManagerAdapter mMockPackageManagerAdapter = Mockito.mock(PackageManagerAdapter.class); private final IQSTileService.Stub mMockTileService = Mockito.mock(IQSTileService.Stub.class); @@ -77,19 +74,7 @@ public class TileLifecycleManagerTest extends SysuiTestCase { // Stub.asInterface will just return itself. when(mMockTileService.queryLocalInterface(anyString())).thenReturn(mMockTileService); - // Default behavior for bind is success and connects as mMockTileService. - when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())) - .thenAnswer( - new Answer<Boolean>() { - @Override - public Boolean answer(InvocationOnMock invocation) { - ServiceConnection connection = - (ServiceConnection) invocation.getArguments()[1]; - connection.onServiceConnected( - mTileServiceComponentName, mMockTileService); - return true; - } - }); + mContext.addMockService(mTileServiceComponentName, mMockTileService); mTileServiceIntent = new Intent().setComponent(mTileServiceComponentName); @@ -97,7 +82,7 @@ public class TileLifecycleManagerTest extends SysuiTestCase { mThread = new HandlerThread("TestThread"); mThread.start(); mHandler = new Handler(mThread.getLooper()); - mStateManager = new TileLifecycleManager(mHandler, mMockContext, + mStateManager = new TileLifecycleManager(mHandler, mContext, Mockito.mock(IQSService.class), new Tile(), mTileServiceIntent, mUser, @@ -126,11 +111,7 @@ public class TileLifecycleManagerTest extends SysuiTestCase { } private void verifyBind(int times) { - verify(mMockContext, times(times)).bindServiceAsUser( - mTileServiceIntent, - mStateManager, - Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, - mUser); + assertEquals(times > 0, mContext.isBound(mTileServiceComponentName)); } @Test @@ -143,7 +124,7 @@ public class TileLifecycleManagerTest extends SysuiTestCase { public void testUnbind() { mStateManager.setBindService(true); mStateManager.setBindService(false); - verify(mMockContext).unbindService(mStateManager); + assertFalse(mContext.isBound(mTileServiceComponentName)); } @Test @@ -203,7 +184,7 @@ public class TileLifecycleManagerTest extends SysuiTestCase { verifyBind(1); mStateManager.setBindService(false); - verify(mMockContext).unbindService(mStateManager); + assertFalse(mContext.isBound(mTileServiceComponentName)); verify(mMockTileService, never()).onStartListening(); } @@ -217,7 +198,7 @@ public class TileLifecycleManagerTest extends SysuiTestCase { verifyBind(1); mStateManager.setBindService(false); - verify(mMockContext).unbindService(mStateManager); + assertFalse(mContext.isBound(mTileServiceComponentName)); verify(mMockTileService, never()).onClick(null); } @@ -233,7 +214,7 @@ public class TileLifecycleManagerTest extends SysuiTestCase { // Package is re-enabled. setPackageEnabled(true); mStateManager.onReceive( - mMockContext, + mContext, new Intent( Intent.ACTION_PACKAGE_CHANGED, Uri.fromParts( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 639c8daf586b..7335af3fff9d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -16,15 +16,18 @@ package com.android.systemui.statusbar; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; -import android.content.ContentResolver; import android.content.Context; -import android.content.pm.PackageManager; -import android.content.res.Resources; +import android.hardware.fingerprint.FingerprintManager; import android.os.Looper; import android.support.test.runner.AndroidJUnit4; -import android.telephony.SubscriptionManager; import android.test.suitebuilder.annotation.SmallTest; import android.view.View; import android.view.ViewGroup; @@ -37,22 +40,15 @@ import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; @SmallTest @RunWith(AndroidJUnit4.class) public class KeyguardIndicationControllerTest extends SysuiTestCase { private final String ORGANIZATION_NAME = "organization"; - private final String DISCLOSURE_WITH_ORGANIZATION_NAME = "managed by organization"; - private Context mContext = mock(Context.class); + private String mDisclosureWithOrganization; + private DevicePolicyManager mDevicePolicyManager = mock(DevicePolicyManager.class); private ViewGroup mIndicationArea = mock(ViewGroup.class); private KeyguardIndicationTextView mDisclosure = mock(KeyguardIndicationTextView.class); @@ -61,19 +57,11 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { @Before public void setUp() throws Exception { - final Resources resources = mock(Resources.class); - when(mContext.getResources()).thenReturn(resources); - when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class)); - when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class)); - when(mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)).thenReturn( - mock(SubscriptionManager.class)); - when(mContext.getSystemService(Context.TRUST_SERVICE)).thenReturn( - mock(TrustManager.class)); - when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn( - mDevicePolicyManager); - - when(resources.getString(R.string.do_disclosure_with_name, ORGANIZATION_NAME)) - .thenReturn(DISCLOSURE_WITH_ORGANIZATION_NAME); + mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager); + mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class)); + mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class)); + mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name, + ORGANIZATION_NAME); when(mIndicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure)) .thenReturn(mDisclosure); @@ -113,7 +101,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { createController(); verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(DISCLOSURE_WITH_ORGANIZATION_NAME); + verify(mDisclosure).switchIndication(mDisclosureWithOrganization); verifyNoMoreInteractions(mDisclosure); } @@ -140,7 +128,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { monitor.onKeyguardVisibilityChanged(true); verify(mDisclosure).setVisibility(View.VISIBLE); - verify(mDisclosure).switchIndication(DISCLOSURE_WITH_ORGANIZATION_NAME); + verify(mDisclosure).switchIndication(mDisclosureWithOrganization); verifyNoMoreInteractions(mDisclosure); reset(mDisclosure); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java index 9a697eedc2e1..87c4c664f352 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java @@ -16,26 +16,24 @@ package com.android.systemui.statusbar.policy; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import android.app.admin.DevicePolicyManager; import android.content.Context; -import android.content.pm.UserInfo; import android.net.ConnectivityManager; -import android.os.UserManager; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import com.android.systemui.SysuiTestCase; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - @SmallTest @RunWith(AndroidJUnit4.class) @@ -45,16 +43,9 @@ public class SecurityControllerTest extends SysuiTestCase { @Before public void setUp() throws Exception { - final Context context = mock(Context.class); - when(context.getSystemService(Context.DEVICE_POLICY_SERVICE)) - .thenReturn(mDevicePolicyManager); - when(context.getSystemService(Context.CONNECTIVITY_SERVICE)) - .thenReturn(mock(ConnectivityManager.class)); - final UserManager userManager = mock(UserManager.class); - when(userManager.getUserInfo(anyInt())).thenReturn(mock(UserInfo.class)); - when(context.getSystemService(Context.USER_SERVICE)) - .thenReturn(userManager); - mSecurityController = new SecurityControllerImpl(context); + mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager); + mContext.addMockSystemService(Context.CONNECTIVITY_SERVICE, mock(ConnectivityManager.class)); + mSecurityController = new SecurityControllerImpl(mContext); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java index bf73416ffcff..a95280641aa1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java @@ -16,15 +16,19 @@ package com.android.systemui.utils; import android.content.BroadcastReceiver; import android.content.ComponentCallbacks; +import android.content.ComponentName; import android.content.ContentProviderClient; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.res.Resources; import android.os.Handler; +import android.os.IBinder; import android.os.UserHandle; import android.provider.Settings; +import android.util.ArrayMap; import com.android.systemui.utils.leaks.Tracker; import com.android.systemui.SysuiTestCase; @@ -34,6 +38,10 @@ public class TestableContext extends ContextWrapper { private final FakeContentResolver mFakeContentResolver; private final FakeSettingsProvider mSettingsProvider; + private ArrayMap<String, Object> mMockSystemServices; + private ArrayMap<ComponentName, IBinder> mMockServices; + private ArrayMap<ServiceConnection, ComponentName> mActiveServices; + private Tracker mReceiver; private Tracker mService; private Tracker mComponent; @@ -51,6 +59,33 @@ public class TestableContext extends ContextWrapper { mComponent = test.getTracker("component"); } + @Override + public Resources getResources() { + return super.getResources(); + } + + public void addMockSystemService(String name, Object service) { + mMockSystemServices = lazyInit(mMockSystemServices); + mMockSystemServices.put(name, service); + } + + public void addMockService(ComponentName component, IBinder service) { + mMockServices = lazyInit(mMockServices); + mMockServices.put(component, service); + } + + private <T, V> ArrayMap<T, V> lazyInit(ArrayMap<T, V> services) { + return services != null ? services : new ArrayMap<T, V>(); + } + + @Override + public Object getSystemService(String name) { + if (mMockSystemServices != null && mMockSystemServices.containsKey(name)) { + return mMockSystemServices.get(name); + } + return super.getSystemService(name); + } + public FakeSettingsProvider getSettingsProvider() { return mSettingsProvider; } @@ -96,6 +131,7 @@ public class TestableContext extends ContextWrapper { @Override public boolean bindService(Intent service, ServiceConnection conn, int flags) { if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable()); + if (checkMocks(service.getComponent(), conn)) return true; return super.bindService(service, conn, flags); } @@ -103,6 +139,7 @@ public class TestableContext extends ContextWrapper { public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user) { if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable()); + if (checkMocks(service.getComponent(), conn)) return true; return super.bindServiceAsUser(service, conn, flags, handler, user); } @@ -110,15 +147,35 @@ public class TestableContext extends ContextWrapper { public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags, UserHandle user) { if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable()); + if (checkMocks(service.getComponent(), conn)) return true; return super.bindServiceAsUser(service, conn, flags, user); } + private boolean checkMocks(ComponentName component, ServiceConnection conn) { + if (mMockServices != null && component != null && mMockServices.containsKey(component)) { + mActiveServices = lazyInit(mActiveServices); + mActiveServices.put(conn, component); + conn.onServiceConnected(component, mMockServices.get(component)); + return true; + } + return false; + } + @Override public void unbindService(ServiceConnection conn) { if (mService != null) mService.getLeakInfo(conn).clearAllocations(); + if (mActiveServices != null && mActiveServices.containsKey(conn)) { + conn.onServiceDisconnected(mActiveServices.get(conn)); + mActiveServices.remove(conn); + return; + } super.unbindService(conn); } + public boolean isBound(ComponentName component) { + return mActiveServices != null && mActiveServices.containsValue(component); + } + @Override public void registerComponentCallbacks(ComponentCallbacks callback) { if (mComponent != null) mComponent.getLeakInfo(callback).addAllocation(new Throwable()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableImageView.java b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableImageView.java new file mode 100644 index 000000000000..b131460e1eff --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableImageView.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.utils; + +import android.annotation.DrawableRes; +import android.annotation.Nullable; +import android.content.Context; +import android.util.AttributeSet; +import android.widget.ImageView; + +public class TestableImageView extends ImageView { + + private int mLastResId = -1; + + public TestableImageView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void setImageResource(@DrawableRes int resId) { + mLastResId = resId; + super.setImageResource(resId); + } + + public int getLastImageResource() { + return mLastResId; + } +} diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java index 2fbdbeb7c660..c0550c6c7876 100644 --- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java @@ -143,7 +143,8 @@ public class NetworkNotificationManager { if (DBG) { Slog.d(TAG, String.format( "showNotification tag=%s event=%s transport=%s extraInfo=%s highPrioriy=%s", - tag, nameOf(eventId), getTransportName(transportType), extraInfo, highPriority)); + tag, nameOf(eventId), getTransportName(transportType), extraInfo, + highPriority)); } Resources r = Resources.getSystem(); @@ -227,13 +228,14 @@ public class NetworkNotificationManager { } final int eventId = mNotificationTypeMap.get(id); if (DBG) { - Slog.d(TAG, String.format("clearing notification tag=%s event=", tag, nameOf(eventId))); + Slog.d(TAG, String.format("clearing notification tag=%s event=%s", tag, + nameOf(eventId))); } try { mNotificationManager.cancelAsUser(tag, eventId, UserHandle.ALL); } catch (NullPointerException npe) { Slog.d(TAG, String.format( - "failed to clear notification tag=%s event=", tag, nameOf(eventId)), npe); + "failed to clear notification tag=%s event=%s", tag, nameOf(eventId)), npe); } mNotificationTypeMap.delete(id); } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 31939749ee66..203f841bb305 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -105,11 +105,11 @@ public class Installer extends SystemService { } } - public void createAppData(String uuid, String packageName, int userId, int flags, int appId, + public long createAppData(String uuid, String packageName, int userId, int flags, int appId, String seInfo, int targetSdkVersion) throws InstallerException { - if (!checkBeforeRemote()) return; + if (!checkBeforeRemote()) return -1; try { - mInstalld.createAppData(uuid, packageName, userId, flags, appId, seInfo, + return mInstalld.createAppData(uuid, packageName, userId, flags, appId, seInfo, targetSdkVersion); } catch (Exception e) { throw InstallerException.from(e); @@ -182,16 +182,6 @@ public class Installer extends SystemService { } } - public long getAppDataInode(String uuid, String packageName, int userId, int flags) - throws InstallerException { - if (!checkBeforeRemote()) return -1; - try { - return mInstalld.getAppDataInode(uuid, packageName, userId, flags); - } catch (Exception e) { - throw InstallerException.from(e); - } - } - public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet, int dexoptNeeded, @Nullable String outputPath, int dexFlags, String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index df1de11a008b..4937662b91d4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -20459,8 +20459,9 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); Preconditions.checkNotNull(app.seinfo); + long ceDataInode = -1; try { - mInstaller.createAppData(volumeUuid, packageName, userId, flags, + ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags, appId, app.seinfo, app.targetSdkVersion); } catch (InstallerException e) { if (app.isSystemApp()) { @@ -20468,7 +20469,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); + ", but trying to recover: " + e); destroyAppDataLeafLIF(pkg, userId, flags); try { - mInstaller.createAppData(volumeUuid, packageName, userId, flags, + ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags, appId, app.seinfo, app.targetSdkVersion); logCriticalInfo(Log.DEBUG, "Recovery succeeded!"); } catch (InstallerException e2) { @@ -20479,21 +20480,13 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } } - if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) { - try { - // CE storage is unlocked right now, so read out the inode and - // remember for use later when it's locked - // TODO: mark this structure as dirty so we persist it! - final long ceDataInode = mInstaller.getAppDataInode(volumeUuid, packageName, userId, - StorageManager.FLAG_STORAGE_CE); - synchronized (mPackages) { - final PackageSetting ps = mSettings.mPackages.get(packageName); - if (ps != null) { - ps.setCeDataInode(ceDataInode, userId); - } + if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) { + // TODO: mark this structure as dirty so we persist it! + synchronized (mPackages) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + if (ps != null) { + ps.setCeDataInode(ceDataInode, userId); } - } catch (InstallerException e) { - Slog.e(TAG, "Failed to find inode for " + packageName + ": " + e); } } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 3645c24043ca..8f64353a362d 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1556,6 +1556,19 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); } final long ident = Binder.clearCallingIdentity(); + + // Live wallpapers can't be specified for keyguard. If we're using a static + // system+lock image currently, migrate the system wallpaper to be a lock-only + // image as part of making a different live component active as the system + // wallpaper. + if (mImageWallpaper.equals(wallpaper.wallpaperComponent)) { + if (mLockWallpaperMap.get(userId) == null) { + // We're using the static imagery and there is no lock-specific image in place, + // therefore it's a shared system+lock image that we need to migrate. + migrateSystemToLockWallpaperLocked(userId); + } + } + try { wallpaper.imageWallpaperPending = false; if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) { diff --git a/services/core/jni/com_android_server_ConsumerIrService.cpp b/services/core/jni/com_android_server_ConsumerIrService.cpp index 5906e6b602ef..1f7bf4a05ad4 100644 --- a/services/core/jni/com_android_server_ConsumerIrService.cpp +++ b/services/core/jni/com_android_server_ConsumerIrService.cpp @@ -49,7 +49,7 @@ static jint halTransmit(JNIEnv *env, jobject /* obj */, jint carrierFrequency, hidl_vec<int32_t> patternVec; patternVec.setToExternal(const_cast<int32_t*>(cPattern.get()), cPattern.size()); - bool success = mHal->transmit(carrierFrequency, patternVec, cPattern.size()); + bool success = mHal->transmit(carrierFrequency, patternVec); return success ? 0 : -1; } |