diff options
75 files changed, 2352 insertions, 391 deletions
diff --git a/api/current.txt b/api/current.txt index a84bfb5f7893..b017f9d2f5b9 100644 --- a/api/current.txt +++ b/api/current.txt @@ -41819,7 +41819,7 @@ package android.view { method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]); method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int); method public android.view.View findNextFocusFromRect(android.view.ViewGroup, android.graphics.Rect, int); - method public android.view.View findNextKeyboardNavigationCluster(android.view.ViewGroup, android.view.View, int); + method public android.view.View findNextKeyboardNavigationCluster(int, android.view.View, android.view.View, int); method public static android.view.FocusFinder getInstance(); } @@ -43112,7 +43112,7 @@ package android.view { method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>); method public void addFocusables(java.util.ArrayList<android.view.View>, int); method public void addFocusables(java.util.ArrayList<android.view.View>, int, int); - method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int); + method public void addKeyboardNavigationClusters(int, java.util.Collection<android.view.View>, int); method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); method public void addTouchables(java.util.ArrayList<android.view.View>); @@ -43400,7 +43400,7 @@ package android.view { method public boolean isVerticalFadingEdgeEnabled(); method public boolean isVerticalScrollBarEnabled(); method public void jumpDrawablesToCurrentState(); - method public android.view.View keyboardNavigationClusterSearch(int); + method public android.view.View keyboardNavigationClusterSearch(int, android.view.View, int); method public void layout(int, int, int, int); method public final void measure(int, int); method protected static int[] mergeDrawableStates(int[], int[]); @@ -43682,6 +43682,8 @@ package android.view { field public static final int FOCUS_BACKWARD = 1; // 0x1 field public static final int FOCUS_DOWN = 130; // 0x82 field public static final int FOCUS_FORWARD = 2; // 0x2 + field public static final int FOCUS_GROUP_CLUSTER = 1; // 0x1 + field public static final int FOCUS_GROUP_SECTION = 2; // 0x2 field public static final int FOCUS_LEFT = 17; // 0x11 field public static final int FOCUS_RIGHT = 66; // 0x42 field public static final int FOCUS_UP = 33; // 0x21 @@ -44049,7 +44051,6 @@ package android.view { method protected deprecated boolean isChildrenDrawnWithCacheEnabled(); method public boolean isMotionEventSplittingEnabled(); method public boolean isTransitionGroup(); - method public android.view.View keyboardNavigationClusterSearch(android.view.View, int); method public final void layout(int, int, int, int); method protected void measureChild(android.view.View, int, int); method protected void measureChildWithMargins(android.view.View, int, int, int, int); @@ -44212,7 +44213,7 @@ package android.view { method public abstract boolean isLayoutRequested(); method public abstract boolean isTextAlignmentResolved(); method public abstract boolean isTextDirectionResolved(); - method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int); + method public abstract android.view.View keyboardNavigationClusterSearch(int, android.view.View, int); method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int); method public abstract boolean onNestedFling(android.view.View, float, float, boolean); method public abstract boolean onNestedPreFling(android.view.View, float, float); diff --git a/api/system-current.txt b/api/system-current.txt index aeb17fbfe530..a5534deebd93 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -66,7 +66,7 @@ package android { field public static final java.lang.String BLUETOOTH_PRIVILEGED = "android.permission.BLUETOOTH_PRIVILEGED"; field public static final java.lang.String BODY_SENSORS = "android.permission.BODY_SENSORS"; field public static final java.lang.String BRICK = "android.permission.BRICK"; - field public static final java.lang.String BROADCAST_NETWORK_PRIVILEGED = "android.permission.BROADCAST_NETWORK_PRIVILEGED"; + field public static final deprecated java.lang.String BROADCAST_NETWORK_PRIVILEGED = "android.permission.BROADCAST_NETWORK_PRIVILEGED"; field public static final java.lang.String BROADCAST_PACKAGE_REMOVED = "android.permission.BROADCAST_PACKAGE_REMOVED"; field public static final java.lang.String BROADCAST_SMS = "android.permission.BROADCAST_SMS"; field public static final java.lang.String BROADCAST_STICKY = "android.permission.BROADCAST_STICKY"; @@ -201,6 +201,7 @@ package android { field public static final java.lang.String REORDER_TASKS = "android.permission.REORDER_TASKS"; field public static final java.lang.String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; field public static final java.lang.String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES"; + field public static final java.lang.String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES"; field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES"; field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT"; field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS"; @@ -45028,7 +45029,7 @@ package android.view { method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]); method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int); method public android.view.View findNextFocusFromRect(android.view.ViewGroup, android.graphics.Rect, int); - method public android.view.View findNextKeyboardNavigationCluster(android.view.ViewGroup, android.view.View, int); + method public android.view.View findNextKeyboardNavigationCluster(int, android.view.View, android.view.View, int); method public static android.view.FocusFinder getInstance(); } @@ -46321,7 +46322,7 @@ package android.view { method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>); method public void addFocusables(java.util.ArrayList<android.view.View>, int); method public void addFocusables(java.util.ArrayList<android.view.View>, int, int); - method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int); + method public void addKeyboardNavigationClusters(int, java.util.Collection<android.view.View>, int); method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); method public void addTouchables(java.util.ArrayList<android.view.View>); @@ -46609,7 +46610,7 @@ package android.view { method public boolean isVerticalFadingEdgeEnabled(); method public boolean isVerticalScrollBarEnabled(); method public void jumpDrawablesToCurrentState(); - method public android.view.View keyboardNavigationClusterSearch(int); + method public android.view.View keyboardNavigationClusterSearch(int, android.view.View, int); method public void layout(int, int, int, int); method public final void measure(int, int); method protected static int[] mergeDrawableStates(int[], int[]); @@ -46891,6 +46892,8 @@ package android.view { field public static final int FOCUS_BACKWARD = 1; // 0x1 field public static final int FOCUS_DOWN = 130; // 0x82 field public static final int FOCUS_FORWARD = 2; // 0x2 + field public static final int FOCUS_GROUP_CLUSTER = 1; // 0x1 + field public static final int FOCUS_GROUP_SECTION = 2; // 0x2 field public static final int FOCUS_LEFT = 17; // 0x11 field public static final int FOCUS_RIGHT = 66; // 0x42 field public static final int FOCUS_UP = 33; // 0x21 @@ -47258,7 +47261,6 @@ package android.view { method protected deprecated boolean isChildrenDrawnWithCacheEnabled(); method public boolean isMotionEventSplittingEnabled(); method public boolean isTransitionGroup(); - method public android.view.View keyboardNavigationClusterSearch(android.view.View, int); method public final void layout(int, int, int, int); method protected void measureChild(android.view.View, int, int); method protected void measureChildWithMargins(android.view.View, int, int, int, int); @@ -47421,7 +47423,7 @@ package android.view { method public abstract boolean isLayoutRequested(); method public abstract boolean isTextAlignmentResolved(); method public abstract boolean isTextDirectionResolved(); - method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int); + method public abstract android.view.View keyboardNavigationClusterSearch(int, android.view.View, int); method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int); method public abstract boolean onNestedFling(android.view.View, float, float, boolean); method public abstract boolean onNestedPreFling(android.view.View, float, float); diff --git a/api/test-current.txt b/api/test-current.txt index ff70fee1d6e8..5db134a28964 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -42106,7 +42106,7 @@ package android.view { method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]); method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int); method public android.view.View findNextFocusFromRect(android.view.ViewGroup, android.graphics.Rect, int); - method public android.view.View findNextKeyboardNavigationCluster(android.view.ViewGroup, android.view.View, int); + method public android.view.View findNextKeyboardNavigationCluster(int, android.view.View, android.view.View, int); method public static android.view.FocusFinder getInstance(); } @@ -43401,7 +43401,7 @@ package android.view { method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>); method public void addFocusables(java.util.ArrayList<android.view.View>, int); method public void addFocusables(java.util.ArrayList<android.view.View>, int, int); - method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int); + method public void addKeyboardNavigationClusters(int, java.util.Collection<android.view.View>, int); method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); method public void addTouchables(java.util.ArrayList<android.view.View>); @@ -43690,7 +43690,7 @@ package android.view { method public boolean isVerticalFadingEdgeEnabled(); method public boolean isVerticalScrollBarEnabled(); method public void jumpDrawablesToCurrentState(); - method public android.view.View keyboardNavigationClusterSearch(int); + method public android.view.View keyboardNavigationClusterSearch(int, android.view.View, int); method public void layout(int, int, int, int); method public final void measure(int, int); method protected static int[] mergeDrawableStates(int[], int[]); @@ -43972,6 +43972,8 @@ package android.view { field public static final int FOCUS_BACKWARD = 1; // 0x1 field public static final int FOCUS_DOWN = 130; // 0x82 field public static final int FOCUS_FORWARD = 2; // 0x2 + field public static final int FOCUS_GROUP_CLUSTER = 1; // 0x1 + field public static final int FOCUS_GROUP_SECTION = 2; // 0x2 field public static final int FOCUS_LEFT = 17; // 0x11 field public static final int FOCUS_RIGHT = 66; // 0x42 field public static final int FOCUS_UP = 33; // 0x21 @@ -44343,7 +44345,6 @@ package android.view { method protected deprecated boolean isChildrenDrawnWithCacheEnabled(); method public boolean isMotionEventSplittingEnabled(); method public boolean isTransitionGroup(); - method public android.view.View keyboardNavigationClusterSearch(android.view.View, int); method public final void layout(int, int, int, int); method protected void measureChild(android.view.View, int, int); method protected void measureChildWithMargins(android.view.View, int, int, int, int); @@ -44506,7 +44507,7 @@ package android.view { method public abstract boolean isLayoutRequested(); method public abstract boolean isTextAlignmentResolved(); method public abstract boolean isTextDirectionResolved(); - method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int); + method public abstract android.view.View keyboardNavigationClusterSearch(int, android.view.View, int); method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int); method public abstract boolean onNestedFling(android.view.View, float, float, boolean); method public abstract boolean onNestedPreFling(android.view.View, float, float); diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java index 5cc127766e83..52cd2de4c2b6 100644 --- a/core/java/android/bluetooth/BluetoothCodecConfig.java +++ b/core/java/android/bluetooth/BluetoothCodecConfig.java @@ -47,7 +47,14 @@ public final class BluetoothCodecConfig implements Parcelable { public static final String EXTRA_PREVIOUS_CODEC_CONFIG = "android.bluetooth.codec.extra.PREVIOUS_CODEC_CONFIG"; + // Add an entry for each source codec here. + // NOTE: The values should be same as those listed in the following file: + // hardware/libhardware/include/hardware/bt_av.h public static final int SOURCE_CODEC_TYPE_SBC = 0; + public static final int SOURCE_CODEC_TYPE_APTX = 1; + public static final int SOURCE_CODEC_TYPE_APTX_HD = 2; + public static final int SOURCE_CODEC_TYPE_LDAC = 3; + public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; public static final int CODEC_PRIORITY_DEFAULT = 0; diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index 7036f87b98f9..e6cae6932402 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -1883,7 +1883,8 @@ public class IntentFilter implements Parcelable { */ } - private IntentFilter(Parcel source) { + /** @hide */ + public IntentFilter(Parcel source) { mActions = new ArrayList<String>(); source.readStringList(mActions); if (source.readInt() != 0) { diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 2236291fd248..cf4c0fae6f16 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -16,6 +16,8 @@ package android.content.pm; +import android.os.Parcel; +import android.os.Parcelable; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; @@ -65,6 +67,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; +import java.lang.reflect.Constructor; import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; @@ -80,6 +83,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.zip.ZipEntry; @@ -307,14 +311,16 @@ public class PackageParser { } } - static class ParseComponentArgs extends ParsePackageItemArgs { + /** @hide */ + @VisibleForTesting + public static class ParseComponentArgs extends ParsePackageItemArgs { final String[] sepProcesses; final int processRes; final int descriptionRes; final int enabledRes; int flags; - ParseComponentArgs(Package _owner, String[] _outError, + public ParseComponentArgs(Package _owner, String[] _outError, int _nameRes, int _labelRes, int _iconRes, int _roundIconRes, int _logoRes, int _bannerRes, String[] _sepProcesses, int _processRes, @@ -874,12 +880,24 @@ public class PackageParser { @VisibleForTesting protected Package fromCacheEntry(byte[] bytes) throws IOException { - return null; + Parcel p = Parcel.obtain(); + p.unmarshall(bytes, 0, bytes.length); + p.setDataPosition(0); + + PackageParser.Package pkg = new PackageParser.Package(p); + p.recycle(); + + return pkg; } @VisibleForTesting protected byte[] toCacheEntry(Package pkg) throws IOException { - return null; + Parcel p = Parcel.obtain(); + pkg.writeToParcel(p, 0 /* flags */); + byte[] serialized = p.marshall(); + p.recycle(); + + return serialized; } /** @@ -1344,6 +1362,11 @@ public class PackageParser { verified = true; } catch (ApkSignatureSchemeV2Verifier.SignatureNotFoundException e) { // No APK Signature Scheme v2 signature found + if ((parseFlags & PARSE_IS_EPHEMERAL) != 0) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "No APK Signature Scheme v2 signature in ephemeral package " + apkPath, + e); + } } catch (Exception e) { // APK Signature Scheme v2 signature was found but did not verify throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, @@ -1516,10 +1539,10 @@ public class PackageParser { final Certificate[][] certificates; if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { // TODO: factor signature related items out of Package object - final Package tempPkg = new Package(null); + final Package tempPkg = new Package((String) null); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); try { - collectCertificates(tempPkg, apkFile, 0 /*parseFlags*/); + collectCertificates(tempPkg, apkFile, flags); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } @@ -3623,6 +3646,13 @@ public class PackageParser { private static boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo, String[] outError, String tag, TypedArray sa, boolean nameRequired, int nameRes, int labelRes, int iconRes, int roundIconRes, int logoRes, int bannerRes) { + // This case can only happen in unit tests where we sometimes need to create fakes + // of various package parser data structures. + if (sa == null) { + outError[0] = tag + " does not contain any attributes"; + return false; + } + String name = sa.getNonConfigurationString(nameRes, 0); if (name == null) { if (nameRequired) { @@ -5113,7 +5143,7 @@ public class PackageParser { * Representation of a full package parsed from APK files on disk. A package * consists of a single base APK, and zero or more split APKs. */ - public final static class Package { + public final static class Package implements Parcelable { public String packageName; @@ -5154,7 +5184,7 @@ public class PackageParser { public boolean baseHardwareAccelerated; // For now we only support one application per package. - public final ApplicationInfo applicationInfo = new ApplicationInfo(); + public ApplicationInfo applicationInfo = new ApplicationInfo(); public final ArrayList<Permission> permissions = new ArrayList<Permission>(0); public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0); @@ -5578,13 +5608,296 @@ public class PackageParser { + Integer.toHexString(System.identityHashCode(this)) + " " + packageName + "}"; } + + @Override + public int describeContents() { + return 0; + } + + public Package(Parcel dest) { + // We use the boot classloader for all classes that we load. + final ClassLoader boot = Object.class.getClassLoader(); + + packageName = dest.readString(); + splitNames = dest.readStringArray(); + volumeUuid = dest.readString(); + codePath = dest.readString(); + baseCodePath = dest.readString(); + splitCodePaths = dest.readStringArray(); + baseRevisionCode = dest.readInt(); + splitRevisionCodes = dest.createIntArray(); + splitFlags = dest.createIntArray(); + splitPrivateFlags = dest.createIntArray(); + baseHardwareAccelerated = (dest.readInt() == 1); + applicationInfo = dest.readParcelable(boot); + + // We don't serialize the "owner" package and the application info object for each of + // these components, in order to save space and to avoid circular dependencies while + // serialization. We need to fix them all up here. + dest.readParcelableList(permissions, boot); + fixupOwner(permissions); + dest.readParcelableList(permissionGroups, boot); + fixupOwner(permissionGroups); + dest.readParcelableList(activities, boot); + fixupOwner(activities); + dest.readParcelableList(receivers, boot); + fixupOwner(receivers); + dest.readParcelableList(providers, boot); + fixupOwner(providers); + dest.readParcelableList(services, boot); + fixupOwner(services); + dest.readParcelableList(instrumentation, boot); + fixupOwner(instrumentation); + + dest.readStringList(requestedPermissions); + protectedBroadcasts = dest.createStringArrayList(); + parentPackage = dest.readParcelable(boot); + + childPackages = new ArrayList<>(); + dest.readParcelableList(childPackages, boot); + if (childPackages.size() == 0) { + childPackages = null; + } + + libraryNames = dest.createStringArrayList(); + usesLibraries = dest.createStringArrayList(); + usesOptionalLibraries = dest.createStringArrayList(); + usesLibraryFiles = dest.readStringArray(); + + preferredActivityFilters = new ArrayList<>(); + dest.readParcelableList(preferredActivityFilters, boot); + if (preferredActivityFilters.size() == 0) { + preferredActivityFilters = null; + } + + mOriginalPackages = dest.createStringArrayList(); + mRealPackage = dest.readString(); + mAdoptPermissions = dest.createStringArrayList(); + mAppMetaData = dest.readBundle(); + mVersionCode = dest.readInt(); + mVersionName = dest.readString(); + mSharedUserId = dest.readString(); + mSharedUserLabel = dest.readInt(); + + mSignatures = (Signature[]) dest.readParcelableArray(boot, Signature.class); + mCertificates = (Certificate[][]) dest.readSerializable(); + + mPreferredOrder = dest.readInt(); + + // long[] packageUsageTimeMillis is not persisted because it isn't information that + // is parsed from the APK. + + // Object mExtras is not persisted because it is not information that is read from + // the APK, rather, it is supplied by callers. + + + configPreferences = new ArrayList<>(); + dest.readParcelableList(configPreferences, boot); + if (configPreferences.size() == 0) { + configPreferences = null; + } + + reqFeatures = new ArrayList<>(); + dest.readParcelableList(reqFeatures, boot); + if (reqFeatures.size() == 0) { + reqFeatures = null; + } + + featureGroups = new ArrayList<>(); + dest.readParcelableList(featureGroups, boot); + if (featureGroups.size() == 0) { + featureGroups = null; + } + + installLocation = dest.readInt(); + coreApp = (dest.readInt() == 1); + mRequiredForAllUsers = (dest.readInt() == 1); + mRestrictedAccountType = dest.readString(); + mRequiredAccountType = dest.readString(); + mOverlayTarget = dest.readString(); + mOverlayPriority = dest.readInt(); + mTrustedOverlay = (dest.readInt() == 1); + mSigningKeys = (ArraySet<PublicKey>) dest.readArraySet(boot); + mUpgradeKeySets = (ArraySet<String>) dest.readArraySet(boot); + + mKeySetMapping = readKeySetMapping(dest); + + cpuAbiOverride = dest.readString(); + use32bitAbi = (dest.readInt() == 1); + restrictUpdateHash = dest.createByteArray(); + } + + /** + * Sets the package owner and the the {@code applicationInfo} for every component + * owner by this package. + */ + private void fixupOwner(List<? extends Component<?>> list) { + if (list != null) { + for (Component<?> c : list) { + c.owner = this; + if (c instanceof Activity) { + ((Activity) c).info.applicationInfo = this.applicationInfo; + } else if (c instanceof Service) { + ((Service) c).info.applicationInfo = this.applicationInfo; + } else if (c instanceof Provider) { + ((Provider) c).info.applicationInfo = this.applicationInfo; + } + } + } + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(packageName); + dest.writeStringArray(splitNames); + dest.writeString(volumeUuid); + dest.writeString(codePath); + dest.writeString(baseCodePath); + dest.writeStringArray(splitCodePaths); + dest.writeInt(baseRevisionCode); + dest.writeIntArray(splitRevisionCodes); + dest.writeIntArray(splitFlags); + dest.writeIntArray(splitPrivateFlags); + dest.writeInt(baseHardwareAccelerated ? 1 : 0); + dest.writeParcelable(applicationInfo, flags); + + dest.writeParcelableList(permissions, flags); + dest.writeParcelableList(permissionGroups, flags); + dest.writeParcelableList(activities, flags); + dest.writeParcelableList(receivers, flags); + dest.writeParcelableList(providers, flags); + dest.writeParcelableList(services, flags); + dest.writeParcelableList(instrumentation, flags); + + dest.writeStringList(requestedPermissions); + dest.writeStringList(protectedBroadcasts); + dest.writeParcelable(parentPackage, flags); + dest.writeParcelableList(childPackages, flags); + dest.writeStringList(libraryNames); + dest.writeStringList(usesLibraries); + dest.writeStringList(usesOptionalLibraries); + dest.writeStringArray(usesLibraryFiles); + + dest.writeParcelableList(preferredActivityFilters, flags); + + dest.writeStringList(mOriginalPackages); + dest.writeString(mRealPackage); + dest.writeStringList(mAdoptPermissions); + dest.writeBundle(mAppMetaData); + dest.writeInt(mVersionCode); + dest.writeString(mVersionName); + dest.writeString(mSharedUserId); + dest.writeInt(mSharedUserLabel); + + dest.writeParcelableArray(mSignatures, flags); + dest.writeSerializable(mCertificates); + + dest.writeInt(mPreferredOrder); + + // long[] packageUsageTimeMillis is not persisted because it isn't information that + // is parsed from the APK. + + // Object mExtras is not persisted because it is not information that is read from + // the APK, rather, it is supplied by callers. + + dest.writeParcelableList(configPreferences, flags); + dest.writeParcelableList(reqFeatures, flags); + dest.writeParcelableList(featureGroups, flags); + + dest.writeInt(installLocation); + dest.writeInt(coreApp ? 1 : 0); + dest.writeInt(mRequiredForAllUsers ? 1 : 0); + dest.writeString(mRestrictedAccountType); + dest.writeString(mRequiredAccountType); + dest.writeString(mOverlayTarget); + dest.writeInt(mOverlayPriority); + dest.writeInt(mTrustedOverlay ? 1 : 0); + dest.writeArraySet(mSigningKeys); + dest.writeArraySet(mUpgradeKeySets); + writeKeySetMapping(dest, mKeySetMapping); + dest.writeString(cpuAbiOverride); + dest.writeInt(use32bitAbi ? 1 : 0); + dest.writeByteArray(restrictUpdateHash); + } + + + /** + * Writes the keyset mapping to the provided package. {@code null} mappings are permitted. + */ + private static void writeKeySetMapping( + Parcel dest, ArrayMap<String, ArraySet<PublicKey>> keySetMapping) { + if (keySetMapping == null) { + dest.writeInt(-1); + return; + } + + final int N = keySetMapping.size(); + dest.writeInt(N); + + for (int i = 0; i < N; i++) { + dest.writeString(keySetMapping.keyAt(i)); + ArraySet<PublicKey> keys = keySetMapping.valueAt(i); + if (keys == null) { + dest.writeInt(-1); + continue; + } + + final int M = keys.size(); + dest.writeInt(M); + for (int j = 0; j < M; j++) { + dest.writeSerializable(keys.valueAt(j)); + } + } + } + + /** + * Reads a keyset mapping from the given parcel at the given data position. May return + * {@code null} if the serialized mapping was {@code null}. + */ + private static ArrayMap<String, ArraySet<PublicKey>> readKeySetMapping(Parcel in) { + final int N = in.readInt(); + if (N == -1) { + return null; + } + + ArrayMap<String, ArraySet<PublicKey>> keySetMapping = new ArrayMap<>(); + for (int i = 0; i < N; ++i) { + String key = in.readString(); + final int M = in.readInt(); + if (M == -1) { + keySetMapping.put(key, null); + continue; + } + + ArraySet<PublicKey> keys = new ArraySet<>(M); + for (int j = 0; j < M; ++j) { + PublicKey pk = (PublicKey) in.readSerializable(); + keys.add(pk); + } + + keySetMapping.put(key, keys); + } + + return keySetMapping; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Package>() { + public Package createFromParcel(Parcel in) { + return new Package(in); + } + + public Package[] newArray(int size) { + return new Package[size]; + } + }; } - public static class Component<II extends IntentInfo> { - public final Package owner; + public static abstract class Component<II extends IntentInfo> { public final ArrayList<II> intents; public final String className; + public Bundle metaData; + public Package owner; ComponentName componentName; String componentShortName; @@ -5655,6 +5968,83 @@ public class PackageParser { return componentName; } + protected Component(Parcel in) { + className = in.readString(); + metaData = in.readBundle(); + intents = createIntentsList(in); + + owner = null; + } + + protected void writeToParcel(Parcel dest, int flags) { + dest.writeString(className); + dest.writeBundle(metaData); + + writeIntentsList(intents, dest, flags); + } + + /** + * <p> + * Implementation note: The serialized form for the intent list also contains the name + * of the concrete class that's stored in the list, and assumes that every element of the + * list is of the same type. This is very similar to the original parcelable mechanism. + * We cannot use that directly because IntentInfo extends IntentFilter, which is parcelable + * and is public API. It also declares Parcelable related methods as final which means + * we can't extend them. The approach of using composition instead of inheritance leads to + * a large set of cascading changes in the PackageManagerService, which seem undesirable. + * + * <p> + * <b>WARNING: </b> The list of objects returned by this function might need to be fixed up + * to make sure their owner fields are consistent. See {@code fixupOwner}. + */ + private static void writeIntentsList(ArrayList<? extends IntentInfo> list, Parcel out, + int flags) { + if (list == null) { + out.writeInt(-1); + return; + } + + final int N = list.size(); + out.writeInt(N); + + // Don't bother writing the component name if the list is empty. + if (N > 0) { + IntentInfo info = list.get(0); + out.writeString(info.getClass().getName()); + + for (int i = 0; i < N;i++) { + list.get(i).writeIntentInfoToParcel(out, flags); + } + } + } + + private static <T extends IntentInfo> ArrayList<T> createIntentsList(Parcel in) { + int N = in.readInt(); + if (N == -1) { + return null; + } + + if (N == 0) { + return new ArrayList<>(0); + } + + String componentName = in.readString(); + final ArrayList<T> intentsList; + try { + final Class<T> cls = (Class<T>) Class.forName(componentName); + final Constructor<T> cons = cls.getConstructor(Parcel.class); + + intentsList = new ArrayList<>(N); + for (int i = 0; i < N; ++i) { + intentsList.add(cons.newInstance(in)); + } + } catch (ReflectiveOperationException ree) { + throw new AssertionError("Unable to construct intent list for: " + componentName); + } + + return intentsList; + } + public void appendComponentShortName(StringBuilder sb) { ComponentName.appendShortString(sb, owner.applicationInfo.packageName, className); } @@ -5669,7 +6059,7 @@ public class PackageParser { } } - public final static class Permission extends Component<IntentInfo> { + public final static class Permission extends Component<IntentInfo> implements Parcelable { public final PermissionInfo info; public boolean tree; public PermissionGroup group; @@ -5694,9 +6084,40 @@ public class PackageParser { + Integer.toHexString(System.identityHashCode(this)) + " " + info.name + "}"; } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeParcelable(info, flags); + dest.writeInt(tree ? 1 : 0); + dest.writeParcelable(group, flags); + } + + private Permission(Parcel in) { + super(in); + final ClassLoader boot = Object.class.getClassLoader(); + info = in.readParcelable(boot); + tree = (in.readInt() == 1); + group = in.readParcelable(boot); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Permission>() { + public Permission createFromParcel(Parcel in) { + return new Permission(in); + } + + public Permission[] newArray(int size) { + return new Permission[size]; + } + }; } - public final static class PermissionGroup extends Component<IntentInfo> { + public final static class PermissionGroup extends Component<IntentInfo> implements Parcelable { public final PermissionGroupInfo info; public PermissionGroup(Package _owner) { @@ -5719,6 +6140,32 @@ public class PackageParser { + Integer.toHexString(System.identityHashCode(this)) + " " + info.name + "}"; } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeParcelable(info, flags); + } + + private PermissionGroup(Parcel in) { + super(in); + info = in.readParcelable(Object.class.getClassLoader()); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator<PermissionGroup>() { + public PermissionGroup createFromParcel(Parcel in) { + return new PermissionGroup(in); + } + + public PermissionGroup[] newArray(int size) { + return new PermissionGroup[size]; + } + }; } private static boolean copyNeeded(int flags, Package p, @@ -5871,7 +6318,7 @@ public class PackageParser { return pgi; } - public final static class Activity extends Component<ActivityIntentInfo> { + public final static class Activity extends Component<ActivityIntentInfo> implements Parcelable { public final ActivityInfo info; public Activity(final ParseComponentArgs args, final ActivityInfo _info) { @@ -5894,6 +6341,36 @@ public class PackageParser { sb.append('}'); return sb.toString(); } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeParcelable(info, flags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); + } + + private Activity(Parcel in) { + super(in); + info = in.readParcelable(Object.class.getClassLoader()); + + for (ActivityIntentInfo aii : intents) { + aii.activity = this; + } + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Activity>() { + public Activity createFromParcel(Parcel in) { + return new Activity(in); + } + + public Activity[] newArray(int size) { + return new Activity[size]; + } + }; } public static final ActivityInfo generateActivityInfo(Activity a, int flags, @@ -5925,7 +6402,7 @@ public class PackageParser { return ai; } - public final static class Service extends Component<ServiceIntentInfo> { + public final static class Service extends Component<ServiceIntentInfo> implements Parcelable { public final ServiceInfo info; public Service(final ParseComponentArgs args, final ServiceInfo _info) { @@ -5948,6 +6425,36 @@ public class PackageParser { sb.append('}'); return sb.toString(); } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeParcelable(info, flags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); + } + + private Service(Parcel in) { + super(in); + info = in.readParcelable(Object.class.getClassLoader()); + + for (ServiceIntentInfo aii : intents) { + aii.service = this; + } + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Service>() { + public Service createFromParcel(Parcel in) { + return new Service(in); + } + + public Service[] newArray(int size) { + return new Service[size]; + } + }; } public static final ServiceInfo generateServiceInfo(Service s, int flags, @@ -5966,7 +6473,7 @@ public class PackageParser { return si; } - public final static class Provider extends Component<ProviderIntentInfo> { + public final static class Provider extends Component<ProviderIntentInfo> implements Parcelable { public final ProviderInfo info; public boolean syncable; @@ -5997,6 +6504,38 @@ public class PackageParser { sb.append('}'); return sb.toString(); } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeParcelable(info, flags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); + dest.writeInt((syncable) ? 1 : 0); + } + + private Provider(Parcel in) { + super(in); + info = in.readParcelable(Object.class.getClassLoader()); + syncable = (in.readInt() == 1); + + for (ProviderIntentInfo aii : intents) { + aii.provider = this; + } + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Provider>() { + public Provider createFromParcel(Parcel in) { + return new Provider(in); + } + + public Provider[] newArray(int size) { + return new Provider[size]; + } + }; } public static final ProviderInfo generateProviderInfo(Provider p, int flags, @@ -6020,7 +6559,8 @@ public class PackageParser { return pi; } - public final static class Instrumentation extends Component<IntentInfo> { + public final static class Instrumentation extends Component<IntentInfo> implements + Parcelable { public final InstrumentationInfo info; public Instrumentation(final ParsePackageItemArgs args, final InstrumentationInfo _info) { @@ -6042,6 +6582,32 @@ public class PackageParser { sb.append('}'); return sb.toString(); } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeParcelable(info, flags); + } + + private Instrumentation(Parcel in) { + super(in); + info = in.readParcelable(Object.class.getClassLoader()); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Instrumentation>() { + public Instrumentation createFromParcel(Parcel in) { + return new Instrumentation(in); + } + + public Instrumentation[] newArray(int size) { + return new Instrumentation[size]; + } + }; } public static final InstrumentationInfo generateInstrumentationInfo( @@ -6055,7 +6621,7 @@ public class PackageParser { return ii; } - public static class IntentInfo extends IntentFilter { + public static abstract class IntentInfo extends IntentFilter { public boolean hasDefault; public int labelRes; public CharSequence nonLocalizedLabel; @@ -6063,10 +6629,36 @@ public class PackageParser { public int logo; public int banner; public int preferred; + + protected IntentInfo() { + } + + protected IntentInfo(Parcel dest) { + super(dest); + hasDefault = (dest.readInt() == 1); + labelRes = dest.readInt(); + nonLocalizedLabel = dest.readCharSequence(); + icon = dest.readInt(); + logo = dest.readInt(); + banner = dest.readInt(); + preferred = dest.readInt(); + } + + + public void writeIntentInfoToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(hasDefault ? 1 : 0); + dest.writeInt(labelRes); + dest.writeCharSequence(nonLocalizedLabel); + dest.writeInt(icon); + dest.writeInt(logo); + dest.writeInt(banner); + dest.writeInt(preferred); + } } public final static class ActivityIntentInfo extends IntentInfo { - public final Activity activity; + public Activity activity; public ActivityIntentInfo(Activity _activity) { activity = _activity; @@ -6081,10 +6673,14 @@ public class PackageParser { sb.append('}'); return sb.toString(); } + + public ActivityIntentInfo(Parcel in) { + super(in); + } } public final static class ServiceIntentInfo extends IntentInfo { - public final Service service; + public Service service; public ServiceIntentInfo(Service _service) { service = _service; @@ -6099,10 +6695,14 @@ public class PackageParser { sb.append('}'); return sb.toString(); } + + public ServiceIntentInfo(Parcel in) { + super(in); + } } public static final class ProviderIntentInfo extends IntentInfo { - public final Provider provider; + public Provider provider; public ProviderIntentInfo(Provider provider) { this.provider = provider; @@ -6117,6 +6717,10 @@ public class PackageParser { sb.append('}'); return sb.toString(); } + + public ProviderIntentInfo(Parcel in) { + super(in); + } } /** diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index e5aeb4b2139d..8d73544602ba 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -39,6 +39,7 @@ import android.util.SparseArray; import android.util.SparseIntArray; import android.util.TimeUtils; import android.view.Display; + import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; @@ -181,6 +182,8 @@ public abstract class BatteryStats implements Parcelable { * * New in version 19: * - Wakelock data (wl) gets current and max times. + * New in version 20: + * - Sensor gets a background counter. */ static final String CHECKIN_VERSION = "20"; @@ -600,10 +603,13 @@ public abstract class BatteryStats implements Parcelable { */ // Magic sensor number for the GPS. public static final int GPS = -10000; - + public abstract int getHandle(); - + public abstract Timer getSensorTime(); + + /** Returns a counter for usage count when in the background. */ + public abstract Counter getSensorBgCount(); } public class Pid { @@ -3318,13 +3324,16 @@ public abstract class BatteryStats implements Parcelable { final Uid.Sensor se = sensors.valueAt(ise); final int sensorNumber = sensors.keyAt(ise); final Timer timer = se.getSensorTime(); + final Counter bgCounter = se.getSensorBgCount(); if (timer != null) { // Convert from microseconds to milliseconds with rounding final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000; final int count = timer.getCountLocked(which); + final int bgCount = bgCounter != null ? bgCounter.getCountLocked(which) : 0; if (totalTime != 0) { - dumpLine(pw, uid, category, SENSOR_DATA, sensorNumber, totalTime, count); + dumpLine(pw, uid, category, SENSOR_DATA, sensorNumber, totalTime, count, + bgCount); } } } @@ -4493,17 +4502,25 @@ public abstract class BatteryStats implements Parcelable { sb.append(": "); final Timer timer = se.getSensorTime(); + final Counter bgCounter = se.getSensorBgCount(); if (timer != null) { // Convert from microseconds to milliseconds with rounding final long totalTime = (timer.getTotalTimeLocked( rawRealtime, which) + 500) / 1000; final int count = timer.getCountLocked(which); + final int bgCount = bgCounter != null ? bgCounter.getCountLocked(which) : 0; //timer.logState(); if (totalTime != 0) { formatTimeMs(sb, totalTime); sb.append("realtime ("); sb.append(count); - sb.append(" times)"); + sb.append(" times"); + if (bgCount > 0) { + sb.append(", "); + sb.append(bgCount); + sb.append(" bg"); + } + sb.append(")"); } else { sb.append("(not used)"); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 0a32f0dd3720..802b1804c71d 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -256,7 +256,7 @@ public class UserManager { * Specifies if managed profiles of this user can be removed, other than by its profile owner. * The default value is <code>false</code>. * <p> - * This restriction can only be set by device owners. + * This restriction has no effect on managed profiles. * * <p>Key for user restrictions. * <p>Type: Boolean @@ -353,8 +353,8 @@ public class UserManager { /** * Specifies if a user is disallowed from adding managed profiles. * <p>The default value for an unmanaged user is <code>false</code>. - * For users with a device owner set, the default is <code>true</code> - * <p>This restriction can only be set by device owners. + * For users with a device owner set, the default is <code>true</code>. + * <p>This restriction has no effect on managed profiles. * * <p>Key for user restrictions. * <p>Type: Boolean diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 5a6940941698..ded715f38426 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -393,6 +393,9 @@ public final class DocumentsContract { * Flag indicating that a document is virtual, and doesn't have byte * representation in the MIME type specified as {@link #COLUMN_MIME_TYPE}. * + * <p><em>Virtual documents must have at least one alternative streamable + * format via {@link DocumentsProvider#openTypedDocument}</em> + * * @see #COLUMN_FLAGS * @see #COLUMN_MIME_TYPE * @see DocumentsProvider#openTypedDocument(String, String, Bundle, diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index 96c2556428b8..584f5fe494e1 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -575,6 +575,8 @@ public abstract class DocumentsProvider extends ContentProvider { * <p> * A provider may perform a conversion if the documents's MIME type is not * matching the specified MIME type filter. + * <p> + * Virtual documents must have at least one streamable format. * * @param documentId the document to return. * @param mimeTypeFilter the MIME type filter for the requested format. May @@ -1044,6 +1046,8 @@ public abstract class DocumentsProvider extends ContentProvider { * {@link #queryDocument(String, String[])} as long as it matches the filter and the document * does not have the {@link Document#FLAG_VIRTUAL_DOCUMENT} flag set. * + * <p>Virtual documents must have at least one streamable format. + * * @see #getStreamTypes(Uri, String) * @see #openTypedDocument(String, String, Bundle, CancellationSignal) */ diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java index 3f3d5190fac7..1a4f0d1ae803 100644 --- a/core/java/android/view/FocusFinder.java +++ b/core/java/android/view/FocusFinder.java @@ -16,12 +16,16 @@ package android.view; +import static android.view.View.FOCUS_GROUP_CLUSTER; +import static android.view.View.FOCUS_GROUP_SECTION; + import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; import android.util.ArrayMap; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.view.View.FocusGroupType; import java.util.ArrayList; import java.util.Collections; @@ -107,21 +111,26 @@ public class FocusFinder { /** * Find the root of the next keyboard navigation cluster after the current one. - * @param root Thew view tree to look inside. Cannot be null + * @param focusGroupType Type of the focus group + * @param root The view tree to look inside. Cannot be null * @param currentCluster The starting point of the search. Null means the default cluster * @param direction Direction to look * @return The next cluster, or null if none exists */ public View findNextKeyboardNavigationCluster( - @NonNull ViewGroup root, @Nullable View currentCluster, int direction) { + @FocusGroupType int focusGroupType, + @NonNull View root, + @Nullable View currentCluster, + int direction) { View next = null; final ArrayList<View> clusters = mTempList; try { clusters.clear(); - root.addKeyboardNavigationClusters(clusters, direction); + root.addKeyboardNavigationClusters(focusGroupType, clusters, direction); if (!clusters.isEmpty()) { - next = findNextKeyboardNavigationCluster(root, currentCluster, clusters, direction); + next = findNextKeyboardNavigationCluster( + focusGroupType, root, currentCluster, clusters, direction); } } finally { clusters.clear(); @@ -197,19 +206,25 @@ public class FocusFinder { } } - private View findNextKeyboardNavigationCluster(ViewGroup root, View currentCluster, - List<View> clusters, int direction) { + private View findNextKeyboardNavigationCluster( + @FocusGroupType int focusGroupType, + View root, + View currentCluster, + List<View> clusters, + int direction) { final int count = clusters.size(); switch (direction) { case View.FOCUS_FORWARD: case View.FOCUS_DOWN: case View.FOCUS_RIGHT: - return getNextKeyboardNavigationCluster(root, currentCluster, clusters, count); + return getNextKeyboardNavigationCluster( + focusGroupType, root, currentCluster, clusters, count); case View.FOCUS_BACKWARD: case View.FOCUS_UP: case View.FOCUS_LEFT: - return getPreviousKeyboardNavigationCluster(root, currentCluster, clusters, count); + return getPreviousKeyboardNavigationCluster( + focusGroupType, root, currentCluster, clusters, count); default: throw new IllegalArgumentException("Unknown direction: " + direction); } @@ -315,8 +330,12 @@ public class FocusFinder { return null; } - private static View getNextKeyboardNavigationCluster(ViewGroup root, View currentCluster, - List<View> clusters, int count) { + private static View getNextKeyboardNavigationCluster( + @FocusGroupType int focusGroupType, + View root, + View currentCluster, + List<View> clusters, + int count) { if (currentCluster == null) { // The current cluster is the default one. // The next cluster after the default one is the first one. @@ -330,12 +349,25 @@ public class FocusFinder { return clusters.get(position + 1); } - // The current cluster is the last one. The next one is the default one, i.e. the root. - return root; + switch (focusGroupType) { + case FOCUS_GROUP_CLUSTER: + // The current cluster is the last one. The next one is the default one, i.e. the + // root. + return root; + case FOCUS_GROUP_SECTION: + // There is no "default section", hence returning the first one. + return clusters.get(0); + default: + throw new IllegalArgumentException("Unknown focus group type: " + focusGroupType); + } } - private static View getPreviousKeyboardNavigationCluster(ViewGroup root, View currentCluster, - List<View> clusters, int count) { + private static View getPreviousKeyboardNavigationCluster( + @FocusGroupType int focusGroupType, + View root, + View currentCluster, + List<View> clusters, + int count) { if (currentCluster == null) { // The current cluster is the default one. // The previous cluster before the default one is the last one. @@ -349,9 +381,17 @@ public class FocusFinder { return clusters.get(position - 1); } - // The current cluster is the first one. The previous one is the default one, i.e. the - // root. - return root; + switch (focusGroupType) { + case FOCUS_GROUP_CLUSTER: + // The current cluster is the first one. The previous one is the default one, i.e. + // the root. + return root; + case FOCUS_GROUP_SECTION: + // There is no "default section", hence returning the last one. + return clusters.get(count - 1); + default: + throw new IllegalArgumentException("Unknown focus group type: " + focusGroupType); + } } /** diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index f51e02932147..aa941b8fcfb4 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1248,6 +1248,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @Retention(RetentionPolicy.SOURCE) public @interface FocusRealDirection {} // Like @FocusDirection, but without forward/backward + /** @hide */ + @IntDef({ + FOCUS_GROUP_CLUSTER, + FOCUS_GROUP_SECTION + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FocusGroupType {} + /** * Use with {@link #focusSearch(int)}. Move focus to the previous selectable * item. @@ -1281,6 +1289,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public static final int FOCUS_DOWN = 0x00000082; /** + * Use with {@link #keyboardNavigationClusterSearch(int, View, int)}. Search for a keyboard + * navigation cluster. + */ + public static final int FOCUS_GROUP_CLUSTER = 1; + + /** + * Use with {@link #keyboardNavigationClusterSearch(int, View, int)}. Search for a keyboard + * navigation section. + */ + public static final int FOCUS_GROUP_SECTION = 2; + + /** * Bits of {@link #getMeasuredWidthAndState()} and * {@link #getMeasuredWidthAndState()} that provide the actual measured size. */ @@ -9096,22 +9116,47 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + final boolean isFocusGroupOfType(@FocusGroupType int focusGroupType) { + switch (focusGroupType) { + case FOCUS_GROUP_CLUSTER: + return isKeyboardNavigationCluster(); + case FOCUS_GROUP_SECTION: + return isKeyboardNavigationSection(); + default: + throw new IllegalArgumentException("Unknown focus group type: " + focusGroupType); + } + } + /** * Find the nearest keyboard navigation cluster in the specified direction. * This does not actually give focus to that cluster. * + * @param focusGroupType Type of the focus group + * @param currentCluster The starting point of the search. Null means the current cluster is not + * found yet * @param direction Direction to look * * @return The nearest keyboard navigation cluster in the specified direction, or null if none * can be found */ - public View keyboardNavigationClusterSearch(int direction) { - if (mParent != null) { - final View currentCluster = isKeyboardNavigationCluster() ? this : null; - return mParent.keyboardNavigationClusterSearch(currentCluster, direction); - } else { - return null; + public View keyboardNavigationClusterSearch( + @FocusGroupType int focusGroupType, View currentCluster, int direction) { + if (isFocusGroupOfType(focusGroupType)) { + currentCluster = this; + } + if (isRootNamespace() + || focusGroupType == FOCUS_GROUP_SECTION && isKeyboardNavigationCluster()) { + // Root namespace means we should consider ourselves the top of the + // tree for cluster searching; otherwise we could be focus searching + // into other tabs. see LocalActivityManager and TabHost for more info. + // In addition, a cluster node works as a root for section searches. + return FocusFinder.getInstance().findNextKeyboardNavigationCluster( + focusGroupType, this, currentCluster, direction); + } else if (mParent != null) { + return mParent.keyboardNavigationClusterSearch( + focusGroupType, currentCluster, direction); } + return null; } /** @@ -9240,11 +9285,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Adds any keyboard navigation cluster roots that are descendants of this view (possibly * including this view if it is a cluster root itself) to views. * + * @param focusGroupType Type of the focus group * @param views Cluster roots found so far * @param direction Direction to look */ - public void addKeyboardNavigationClusters(@NonNull Collection<View> views, int direction) { - if (!isKeyboardNavigationCluster()) { + public void addKeyboardNavigationClusters( + @FocusGroupType int focusGroupType, + @NonNull Collection<View> views, + int direction) { + if (!(isFocusGroupOfType(focusGroupType))) { return; } views.add(this); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 7c133ac5b7a9..7835899d1419 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -902,23 +902,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override - public View keyboardNavigationClusterSearch(View currentCluster, int direction) { - if (isKeyboardNavigationCluster()) { - currentCluster = this; - } - if (isRootNamespace()) { - // root namespace means we should consider ourselves the top of the - // tree for cluster searching; otherwise we could be focus searching - // into other tabs. see LocalActivityManager and TabHost for more info - return FocusFinder.getInstance().findNextKeyboardNavigationCluster( - this, currentCluster, direction); - } else if (mParent != null) { - return mParent.keyboardNavigationClusterSearch(currentCluster, direction); - } - return null; - } - - @Override public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { return false; } @@ -1164,10 +1147,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override - public void addKeyboardNavigationClusters(Collection<View> views, int direction) { + public void addKeyboardNavigationClusters( + @FocusGroupType int focusGroupType, Collection<View> views, int direction) { final int focusableCount = views.size(); - super.addKeyboardNavigationClusters(views, direction); + super.addKeyboardNavigationClusters(focusGroupType, views, direction); if (focusableCount != views.size()) { // No need to look for clusters inside a cluster. @@ -1183,8 +1167,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager for (int i = 0; i < count; i++) { final View child = children[i]; + if (focusGroupType == FOCUS_GROUP_SECTION && child.isKeyboardNavigationCluster()) { + // When the current cluster is the default cluster, and we are searching for + // sections, ignore sections inside non-default clusters. + continue; + } if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { - child.addKeyboardNavigationClusters(views, direction); + child.addKeyboardNavigationClusters(focusGroupType, views, direction); } } } diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java index 79b05cdb6e50..c5414e925e0e 100644 --- a/core/java/android/view/ViewParent.java +++ b/core/java/android/view/ViewParent.java @@ -18,6 +18,7 @@ package android.view; import android.graphics.Rect; import android.os.Bundle; +import android.view.View.FocusGroupType; import android.view.accessibility.AccessibilityEvent; /** @@ -150,6 +151,7 @@ public interface ViewParent { * Find the nearest keyboard navigation cluster in the specified direction. * This does not actually give focus to that cluster. * + * @param focusGroupType Type of the focus group * @param currentCluster The starting point of the search. Null means the current cluster is not * found yet * @param direction Direction to look @@ -157,7 +159,8 @@ public interface ViewParent { * @return The nearest keyboard navigation cluster in the specified direction, or null if none * can be found */ - View keyboardNavigationClusterSearch(View currentCluster, int direction); + View keyboardNavigationClusterSearch( + @FocusGroupType int focusGroupType, View currentCluster, int direction); /** * Change the z order of the child so it's on top of all other children. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4f2020311ff4..75ebbb70dfd2 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -16,6 +16,8 @@ package android.view; +import static android.view.View.FOCUS_GROUP_CLUSTER; +import static android.view.View.FOCUS_GROUP_SECTION; import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER; import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; @@ -71,6 +73,7 @@ import android.util.TimeUtils; import android.util.TypedValue; import android.view.Surface.OutOfResourcesException; import android.view.View.AttachInfo; +import android.view.View.FocusGroupType; import android.view.View.MeasureSpec; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -4392,11 +4395,12 @@ public final class ViewRootImpl implements ViewParent, return false; } - private boolean performClusterNavigation(int direction) { + private boolean performClusterNavigation( + @FocusGroupType int focusGroupType, int direction) { final View focused = mView.findFocus(); final View cluster = focused != null - ? focused.keyboardNavigationClusterSearch(direction) - : keyboardNavigationClusterSearch(null, direction); + ? focused.keyboardNavigationClusterSearch(focusGroupType, null, direction) + : keyboardNavigationClusterSearch(focusGroupType, null, direction); if (cluster != null && cluster.restoreLastFocus()) { return true; @@ -4418,15 +4422,32 @@ public final class ViewRootImpl implements ViewParent, } int clusterNavigationDirection = 0; + @FocusGroupType int focusGroupType = 0; if (event.getAction() == KeyEvent.ACTION_DOWN && event.isCtrlPressed()) { final int character = event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_CTRL_MASK); if (character == '+') { + focusGroupType = FOCUS_GROUP_CLUSTER; clusterNavigationDirection = View.FOCUS_FORWARD; } if (character == '_') { + focusGroupType = FOCUS_GROUP_CLUSTER; + clusterNavigationDirection = View.FOCUS_BACKWARD; + } + } + + if (event.getAction() == KeyEvent.ACTION_DOWN && event.isAltPressed()) { + final int character = + event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_ALT_MASK); + if (character == '+') { + focusGroupType = FOCUS_GROUP_SECTION; + clusterNavigationDirection = View.FOCUS_FORWARD; + } + + if (character == '_') { + focusGroupType = FOCUS_GROUP_SECTION; clusterNavigationDirection = View.FOCUS_BACKWARD; } } @@ -4456,7 +4477,7 @@ public final class ViewRootImpl implements ViewParent, // Handle automatic focus changes. if (event.getAction() == KeyEvent.ACTION_DOWN) { if (clusterNavigationDirection != 0) { - if (performClusterNavigation(clusterNavigationDirection)) { + if (performClusterNavigation(focusGroupType, clusterNavigationDirection)) { return FINISH_HANDLED; } } else { @@ -4552,8 +4573,8 @@ public final class ViewRootImpl implements ViewParent, if (mPointerIconType != pointerType) { mPointerIconType = pointerType; + mCustomPointerIcon = null; if (mPointerIconType != PointerIcon.TYPE_CUSTOM) { - mCustomPointerIcon = null; InputManager.getInstance().setPointerIconType(pointerType); return true; } @@ -5887,13 +5908,11 @@ public final class ViewRootImpl implements ViewParent, * {@inheritDoc} */ @Override - public View keyboardNavigationClusterSearch(View currentCluster, int direction) { + public View keyboardNavigationClusterSearch( + @FocusGroupType int focusGroupType, View currentCluster, int direction) { checkThread(); - if (!(mView instanceof ViewGroup)) { - return null; - } - return FocusFinder.getInstance().findNextKeyboardNavigationCluster( - (ViewGroup) mView, currentCluster, direction); + return FocusFinder.getInstance().findNextKeyboardNavigationCluster(focusGroupType, + mView, currentCluster, direction); } public void debug() { diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java index e0d589a8a8fb..2d6f44352ba7 100644 --- a/core/java/android/webkit/WebViewZygote.java +++ b/core/java/android/webkit/WebViewZygote.java @@ -24,6 +24,8 @@ import android.os.ZygoteProcess; import android.text.TextUtils; import android.util.Log; +import com.android.internal.annotations.GuardedBy; + import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -38,70 +40,104 @@ public class WebViewZygote { private static final String WEBVIEW_ZYGOTE_SERVICE_32 = "webview_zygote32"; private static final String WEBVIEW_ZYGOTE_SERVICE_64 = "webview_zygote64"; + /** + * Lock object that protects all other static members. + */ + private static final Object sLock = new Object(); + + /** + * Instance that maintains the socket connection to the zygote. This is null if the zygote + * is not running or is not connected. + */ + @GuardedBy("sLock") private static ZygoteProcess sZygote; + /** + * Information about the selected WebView package. This is set from #onWebViewProviderChanged(). + */ + @GuardedBy("sLock") private static PackageInfo sPackage; + /** + * Flag for whether multi-process WebView is enabled. If this is false, the zygote + * will not be started. + */ + @GuardedBy("sLock") private static boolean sMultiprocessEnabled = false; public static ZygoteProcess getProcess() { - connectToZygoteIfNeeded(); - return sZygote; + synchronized (sLock) { + connectToZygoteIfNeededLocked(); + return sZygote; + } } public static String getPackageName() { - return sPackage.packageName; + synchronized (sLock) { + return sPackage.packageName; + } } public static boolean isMultiprocessEnabled() { - return sMultiprocessEnabled && sPackage != null; + synchronized (sLock) { + return sMultiprocessEnabled && sPackage != null; + } } public static void setMultiprocessEnabled(boolean enabled) { - sMultiprocessEnabled = enabled; - - // When toggling between multi-process being on/off, start or stop the - // service. If it is enabled and the zygote is not yet started, bring up the service. - // Otherwise, bring down the service. The name may be null if the package - // information has not yet been resolved. - final String serviceName = getServiceName(); - if (serviceName == null) return; - - if (enabled && sZygote == null) { - SystemService.start(serviceName); - } else { - SystemService.stop(serviceName); - sZygote = null; + synchronized (sLock) { + sMultiprocessEnabled = enabled; + + // When toggling between multi-process being on/off, start or stop the + // service. If it is enabled and the zygote is not yet started, bring up the service. + // Otherwise, bring down the service. The name may be null if the package + // information has not yet been resolved. + final String serviceName = getServiceNameLocked(); + if (serviceName == null) return; + + if (enabled && sZygote == null) { + SystemService.start(serviceName); + } else { + SystemService.stop(serviceName); + sZygote = null; + } } } public static void onWebViewProviderChanged(PackageInfo packageInfo) { - sPackage = packageInfo; - - // If multi-process is not enabled, then do not start the zygote service. - if (!sMultiprocessEnabled) { - return; - } + String serviceName; + synchronized (sLock) { + sPackage = packageInfo; - final String serviceName = getServiceName(); + // If multi-process is not enabled, then do not start the zygote service. + if (!sMultiprocessEnabled) { + return; + } - if (SystemService.isStopped(serviceName)) { - SystemService.start(serviceName); - } else if (sZygote != null) { - SystemService.restart(serviceName); - } + serviceName = getServiceNameLocked(); + sZygote = null; - try { - SystemService.waitForState(serviceName, SystemService.State.RUNNING, 5000); - } catch (TimeoutException e) { - Log.e(LOGTAG, "Timed out waiting for " + serviceName); - return; + // The service may enter the RUNNING state before it opens the socket, + // so connectToZygoteIfNeededLocked() may still fail. + if (SystemService.isStopped(serviceName)) { + SystemService.start(serviceName); + } else { + SystemService.restart(serviceName); + } + + try { + SystemService.waitForState(serviceName, SystemService.State.RUNNING, 5000); + } catch (TimeoutException e) { + Log.e(LOGTAG, "Timed out waiting for " + serviceName); + return; + } + + connectToZygoteIfNeededLocked(); } - - connectToZygoteIfNeeded(); } - private static String getServiceName() { + @GuardedBy("sLock") + private static String getServiceNameLocked() { if (sPackage == null) return null; @@ -113,7 +149,8 @@ public class WebViewZygote { return WEBVIEW_ZYGOTE_SERVICE_32; } - private static void connectToZygoteIfNeeded() { + @GuardedBy("sLock") + private static void connectToZygoteIfNeededLocked() { if (sZygote != null) return; @@ -122,7 +159,7 @@ public class WebViewZygote { return; } - final String serviceName = getServiceName(); + final String serviceName = getServiceNameLocked(); if (!SystemService.isRunning(serviceName)) { Log.e(LOGTAG, serviceName + " is not running"); return; diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 6a5bbccb48f0..5d49d12554e3 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -6912,6 +6912,8 @@ public class BatteryStatsImpl extends BatteryStats { final int mHandle; StopwatchTimer mTimer; + Counter mBgCounter; + public Sensor(BatteryStatsImpl bsi, Uid uid, int handle) { mBsi = bsi; mUid = uid; @@ -6931,7 +6933,17 @@ public class BatteryStatsImpl extends BatteryStats { return new StopwatchTimer(mBsi.mClocks, mUid, 0, pool, timeBase, in); } + private Counter readCounterFromParcel(TimeBase timeBase, Parcel in) { + if (in.readInt() == 0) { + return null; + } + return new Counter(timeBase, in); + } + boolean reset() { + if (mBgCounter != null) { + mBgCounter.reset(true); + } if (mTimer.reset(true)) { mTimer = null; return true; @@ -6941,10 +6953,12 @@ public class BatteryStatsImpl extends BatteryStats { void readFromParcelLocked(TimeBase timeBase, Parcel in) { mTimer = readTimerFromParcel(timeBase, in); + mBgCounter = readCounterFromParcel(timeBase, in); } void writeToParcelLocked(Parcel out, long elapsedRealtimeUs) { Timer.writeTimerToParcel(out, mTimer, elapsedRealtimeUs); + Counter.writeCounterToParcel(out, mBgCounter); } @Override @@ -6953,6 +6967,11 @@ public class BatteryStatsImpl extends BatteryStats { } @Override + public Counter getSensorBgCount() { + return mBgCounter; + } + + @Override public int getHandle() { return mHandle; } @@ -7795,6 +7814,22 @@ public class BatteryStatsImpl extends BatteryStats { return t; } + public Counter getSensorBgCounterLocked(int sensor, boolean create) { + Sensor se = mSensorStats.get(sensor); + if (se == null) { + if (!create) { + return null; + } + se = new Sensor(mBsi, this, sensor); + mSensorStats.put(sensor, se); + } + Counter c = se.mBgCounter; + if (c != null) return c; + c = new Counter(mBsi.mOnBatteryTimeBase); + se.mBgCounter = c; + return c; + } + public void noteStartSyncLocked(String name, long elapsedRealtimeMs) { StopwatchTimer t = mSyncStats.startObject(name); if (t != null) { @@ -7871,6 +7906,10 @@ public class BatteryStatsImpl extends BatteryStats { if (t != null) { t.startRunningLocked(elapsedRealtimeMs); } + Counter c = getSensorBgCounterLocked(sensor, true); + if (c != null && mProcessState >= PROCESS_STATE_BACKGROUND) { + c.stepAtomic(); + } } public void noteStopSensor(int sensor, long elapsedRealtimeMs) { @@ -7886,6 +7925,10 @@ public class BatteryStatsImpl extends BatteryStats { if (t != null) { t.startRunningLocked(elapsedRealtimeMs); } + Counter c = getSensorBgCounterLocked(Sensor.GPS, true); + if (c != null && mProcessState >= PROCESS_STATE_BACKGROUND) { + c.stepAtomic(); + } } public void noteStopGps(long elapsedRealtimeMs) { diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java index a8a55499f5f3..12d699d2ae15 100644 --- a/core/java/com/android/internal/os/WebViewZygoteInit.java +++ b/core/java/com/android/internal/os/WebViewZygoteInit.java @@ -54,6 +54,11 @@ class WebViewZygoteInit { } @Override + protected void maybePreload() { + // Do nothing, we don't need to call ZygoteInit.maybePreload() for the WebView zygote. + } + + @Override protected boolean handlePreloadPackage(String packagePath, String libsPath) { // Ask ApplicationLoaders to create and cache a classloader for the WebView APK so that // our children will reuse the same classloader instead of creating their own. diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index e9e642a58a33..345350cc45fb 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -171,7 +171,7 @@ class ZygoteConnection { return handleAbiListQuery(); } - ZygoteInit.maybePreload(); + maybePreload(); if (parsedArgs.preloadPackage != null) { return handlePreloadPackage(parsedArgs.preloadPackage, @@ -279,6 +279,10 @@ class ZygoteConnection { } } + protected void maybePreload() { + ZygoteInit.maybePreload(); + } + protected boolean handlePreloadPackage(String packagePath, String libsPath) { throw new RuntimeException("Zyogte does not support package preloading"); } diff --git a/core/jni/Android.mk b/core/jni/Android.mk index a13ebaf55cf4..252f168040df 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -215,6 +215,7 @@ LOCAL_C_INCLUDES += \ external/skia/src/effects \ external/skia/src/image \ external/skia/src/images \ + external/skia/src/utils \ external/sqlite/dist \ external/sqlite/android \ external/tremor/Tremor \ diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 1165a45c1535..19d4848e1656 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -8,6 +8,7 @@ #include "SkBRDAllocator.h" #include "SkFrontBufferedStream.h" #include "SkMath.h" +#include "SkOpts.h" #include "SkPixelRef.h" #include "SkStream.h" #include "SkUtils.h" @@ -224,6 +225,45 @@ static bool needsFineScale(const SkISize fullSize, const SkISize decodedSize, needsFineScale(fullSize.height(), decodedSize.height(), sampleSize); } +static inline SkAlphaType computeDecodeAlphaType(SkColorType colorType, SkAlphaType alphaType) { +#ifndef ANDROID_ENABLE_LINEAR_BLENDING + // Skia premultiplies linearly. Until the framework enables linear blending, + // it expects a legacy premultiply. + if (kPremul_SkAlphaType == alphaType && kRGBA_F16_SkColorType != colorType) { + return kUnpremul_SkAlphaType; + } +#endif + + return alphaType; +} + +static inline void premultiplyIfNecessary(SkBitmap* bitmap, SkPMColor* colorPtr, int* colorCount, + SkAlphaType alphaType, bool requireUnpremultiplied) { +#ifndef ANDROID_ENABLE_LINEAR_BLENDING + if (kUnpremul_SkAlphaType != alphaType || requireUnpremultiplied) { + return; + } + + switch (bitmap->colorType()) { + case kN32_SkColorType: + for (int y = 0; y < bitmap->height(); y++) { + SkOpts::RGBA_to_rgbA(bitmap->getAddr32(0, y), bitmap->getAddr32(0, y), + bitmap->width()); + } + + return; + case kIndex_8_SkColorType: + SkOpts::RGBA_to_rgbA(colorPtr, colorPtr, *colorCount); + return; + default: + // kRGBA_F16 will be premultiplied by the codec if necessary. + // kGray_8 (alias kAlpha_8) and k565 are opaque. + LOG_ALWAYS_FATAL("Should be unreachable - no need for legacy premultiply."); + return; + } +#endif +} + static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) { // This function takes ownership of the input stream. Since the SkAndroidCodec // will take ownership of the stream, we don't necessarily need to take ownership @@ -391,13 +431,17 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding colorCount = &maxColors; } - // Set the alpha type for the decode. SkAlphaType alphaType = codec->computeOutputAlphaType(requireUnpremultiplied); + SkAlphaType decodeAlphaType = computeDecodeAlphaType(decodeColorType, alphaType); const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(), - decodeColorType, alphaType, GraphicsJNI::colorSpaceForType(decodeColorType)); + decodeColorType, decodeAlphaType, codec->computeOutputColorSpace(decodeColorType)); + + // When supported by the colorType, we will decode to sRGB (or linear sRGB). However, + // we only want to mark the bitmap as sRGB when linear blending is enabled. + SkImageInfo bitmapInfo = decodeInfo.makeAlphaType(alphaType) + .makeColorSpace(GraphicsJNI::colorSpaceForType(decodeColorType)); - SkImageInfo bitmapInfo = decodeInfo; if (decodeColorType == kGray_8_SkColorType) { // The legacy implementation of BitmapFactory used kAlpha8 for // grayscale images (before kGray8 existed). While the codec @@ -433,6 +477,8 @@ static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding default: return nullObjectReturn("codec->getAndroidPixels() failed."); } + premultiplyIfNecessary(&decodingBitmap, colorPtr, colorCount, decodeAlphaType, + requireUnpremultiplied); jbyteArray ninePatchChunk = NULL; if (peeker.mPatch != NULL) { diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp index c456d622ab5a..81199fa41e1b 100644 --- a/core/jni/android_os_HwBinder.cpp +++ b/core/jni/android_os_HwBinder.cpp @@ -325,15 +325,9 @@ static jobject JHwBinder_native_getService( return NULL; } - sp<hardware::IBinder> service; - manager->get( - ifaceName, - serviceName, - [&service](sp<hidl::base::V1_0::IBase> out) { - service = hardware::toBinder< - hidl::base::V1_0::IBase, hidl::base::V1_0::BpBase - >(out); - }); + sp<hidl::base::V1_0::IBase> base = manager->get(ifaceName, serviceName); + sp<hardware::IBinder> service = hardware::toBinder< + hidl::base::V1_0::IBase, hidl::base::V1_0::BpBase>(base); env->ReleaseStringUTFChars(ifaceNameObj, ifaceName); ifaceName = NULL; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 4c9fb0455196..4b4fd846df08 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1255,6 +1255,12 @@ <permission android:name="android.permission.SCORE_NETWORKS" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows applications to request network + recommendations and scores from the NetworkScoreService. + <p>Not for use by third-party applications. @hide --> + <permission android:name="android.permission.REQUEST_NETWORK_SCORES" + android:protectionLevel="signature|privileged" /> + <!-- ======================================= --> <!-- Permissions for short range, peripheral networks --> <!-- ======================================= --> @@ -2674,7 +2680,10 @@ android:protectionLevel="signature" /> <!-- @SystemApi Allows an application to broadcast privileged networking requests. - <p>Not for use by third-party applications. @hide --> + <p>Not for use by third-party applications. + @hide + @deprecated Use {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} instead + --> <permission android:name="android.permission.BROADCAST_NETWORK_PRIVILEGED" android:protectionLevel="signature|privileged" /> diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml index 53b7ab72481b..abd1bfdb5944 100644 --- a/core/res/res/values-mk-rMK/strings.xml +++ b/core/res/res/values-mk-rMK/strings.xml @@ -201,9 +201,9 @@ <string name="reboot_to_update_title" msgid="6212636802536823850">"Системско ажурирање на Android"</string> <string name="reboot_to_update_prepare" msgid="6305853831955310890">"Се подготвува ажурирањето…"</string> <string name="reboot_to_update_package" msgid="3871302324500927291">"Пакетот за ажурирање се обработува..."</string> - <string name="reboot_to_update_reboot" msgid="6428441000951565185">"Се престартува…"</string> + <string name="reboot_to_update_reboot" msgid="6428441000951565185">"Се рестартира…"</string> <string name="reboot_to_reset_title" msgid="4142355915340627490">"Ресетирање фабрички податоци"</string> - <string name="reboot_to_reset_message" msgid="2432077491101416345">"Се престартува…"</string> + <string name="reboot_to_reset_message" msgid="2432077491101416345">"Се рестартира…"</string> <string name="shutdown_progress" msgid="2281079257329981203">"Се исклучува..."</string> <string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Вашиот таблет ќе се исклучи."</string> <string name="shutdown_confirm" product="tv" msgid="476672373995075359">"Вашиот телевизор ќе се исклучи."</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 71b974725e80..51519a71bab8 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -1267,7 +1267,7 @@ <string name="progress_erasing" product="default" msgid="6596988875507043042">"Apagando cartão SD..."</string> <string name="share" msgid="1778686618230011964">"Compartilhar"</string> <string name="find" msgid="4808270900322985960">"Localizar"</string> - <string name="websearch" msgid="4337157977400211589">"Pesquisa na web do Google"</string> + <string name="websearch" msgid="4337157977400211589">"Pesquisa Google na Web"</string> <string name="find_next" msgid="5742124618942193978">"Localizar próximo"</string> <string name="find_previous" msgid="2196723669388360506">"Localizar anterior"</string> <string name="gpsNotifTicker" msgid="5622683912616496172">"Solicitação de local de <xliff:g id="NAME">%s</xliff:g>"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 71b974725e80..51519a71bab8 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -1267,7 +1267,7 @@ <string name="progress_erasing" product="default" msgid="6596988875507043042">"Apagando cartão SD..."</string> <string name="share" msgid="1778686618230011964">"Compartilhar"</string> <string name="find" msgid="4808270900322985960">"Localizar"</string> - <string name="websearch" msgid="4337157977400211589">"Pesquisa na web do Google"</string> + <string name="websearch" msgid="4337157977400211589">"Pesquisa Google na Web"</string> <string name="find_next" msgid="5742124618942193978">"Localizar próximo"</string> <string name="find_previous" msgid="2196723669388360506">"Localizar anterior"</string> <string name="gpsNotifTicker" msgid="5622683912616496172">"Solicitação de local de <xliff:g id="NAME">%s</xliff:g>"</string> diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java new file mode 100644 index 000000000000..0bdf7caa8903 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java @@ -0,0 +1,66 @@ +/* + * 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.internal.os; + +import android.app.ActivityManager; +import android.os.BatteryStats; +import android.os.Debug; +import android.support.test.filters.SmallTest; +import android.util.Log; + +import junit.framework.TestCase; + +/** + * Test BatteryStatsImpl Sensor Timers. + */ +public class BatteryStatsSensorTest extends TestCase { + + private static final int UID = 10500; + private static final int SENSOR_ID = -10000; + + @SmallTest + public void testSensorStartStop() throws Exception { + final MockClocks clocks = new MockClocks(); + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + bi.mForceOnBattery = true; + clocks.realtime = 100; + clocks.uptime = 100; + bi.getOnBatteryTimeBase().setRunning(true, 100, 100); + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_CACHED_EMPTY); + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND); + bi.noteStartSensorLocked(UID, SENSOR_ID); + clocks.realtime = 200; + clocks.uptime = 200; + bi.noteStopSensorLocked(UID, SENSOR_ID); + + bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_RECEIVER); + bi.noteStartSensorLocked(UID, SENSOR_ID); + clocks.realtime = 400; + clocks.uptime = 400; + bi.noteStopSensorLocked(UID, SENSOR_ID); + + BatteryStats.Timer sensorTimer = bi.getUidStats().get(UID).getSensorStats() + .get(SENSOR_ID).getSensorTime(); + BatteryStats.Counter sensorBgCounter = bi.getUidStats().get(UID).getSensorStats() + .get(SENSOR_ID).getSensorBgCount(); + + assertEquals(2, sensorTimer.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + assertEquals(300000, + sensorTimer.getTotalTimeLocked(clocks.realtime, BatteryStats.STATS_SINCE_CHARGED)); + + assertEquals(1, sensorBgCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED)); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java index 9518219f8c6f..c7cd0ee710e1 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -11,6 +11,7 @@ import org.junit.runners.Suite; BatteryStatsTimeBaseTest.class, BatteryStatsTimerTest.class, BatteryStatsUidTest.class, + BatteryStatsSensorTest.class, }) public class BatteryStatsTests { } diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index 392448976229..10541060398a 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -16,36 +16,28 @@ package com.android.internal.os; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; - -import android.os.BatteryStats; -import android.os.Parcel; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; - -import junit.framework.Assert; -import junit.framework.TestCase; - -import com.android.internal.os.BatteryStatsImpl; - -import org.mockito.Mockito; - /** * Mocks a BatteryStatsImpl object. */ public class MockBatteryStatsImpl extends BatteryStatsImpl { public BatteryStatsImpl.Clocks clocks; + public boolean mForceOnBattery; - MockBatteryStatsImpl() { - super(new MockClocks()); + MockBatteryStatsImpl(Clocks clocks) { + super(clocks); this.clocks = mClocks; } + MockBatteryStatsImpl() { + this(new MockClocks()); + } + public TimeBase getOnBatteryTimeBase() { return mOnBatteryTimeBase; } + public boolean isOnBattery() { + return mForceOnBattery ? true : super.isOnBattery(); + } } diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 393ef0dfe44b..432e77ccd720 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -46,7 +46,7 @@ interface IAudioService { // frameworks/native/include/audiomanager/IAudioManager.h must be updated to match the order // in this file. // - // When a method's argument list is changed, BnAudioManager's corresponding serialization code + // When a method's argument list is changed, BpAudioManager's corresponding serialization code // (if any) in frameworks/native/services/audiomanager/IAudioManager.cpp must be updated. oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags, diff --git a/packages/SettingsLib/res/values-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml index 817bb60c22bb..bcd56d5a82c9 100644 --- a/packages/SettingsLib/res/values-hy-rAM/strings.xml +++ b/packages/SettingsLib/res/values-hy-rAM/strings.xml @@ -170,7 +170,7 @@ <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Միշտ թույլատրել Wi‑Fi ռոումինգի որոնումը"</string> <string name="mobile_data_always_on" msgid="7745605759775320362">"Բջջային տվյալները՝ միշտ ակտիվացրած"</string> <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Անջատել ձայնի բացարձակ ուժգնությունը"</string> - <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Ցույց տալ անլար էկրանի վկայագրման ընտրանքները"</string> + <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Ցույց տալ անլար էկրանի հավաստագրման ընտրանքները"</string> <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Բարձրացնել մակարդակը, Wi‑Fi ընտրիչում ամեն մի SSID-ի համար ցույց տալ RSSI"</string> <string name="wifi_aggressive_handover_summary" msgid="6328455667642570371">"Եթե այս գործառույթը միացված է, Wi‑Fi-ի թույլ ազդանշանի դեպքում Wi‑Fi ինտերնետից անցումը բջջային ինտերնետին ավելի կտրուկ կլինի"</string> <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Թույլատրել/արգելել Wi‑Fi ռոումինգի որոնումը՝ կախված միջերեսում տվյալների երթևեկի ծավալից"</string> diff --git a/packages/SettingsLib/res/values/config.xml b/packages/SettingsLib/res/values/config.xml index 64f21b50c0bc..ee69b56ef472 100755 --- a/packages/SettingsLib/res/values/config.xml +++ b/packages/SettingsLib/res/values/config.xml @@ -26,6 +26,9 @@ <!-- Whether to send a custom package name with the PSD.--> <bool name="config_sendPackageName">false</bool> + <!-- Whether to enable the left nav drawer in all Settings UI.--> + <bool name="config_enable_nav_drawer">false</bool> + <!-- Name for the set of keys associating package names --> <string name="config_helpPackageNameKey" translatable="false"></string> diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index e0490792001e..6179244a27f6 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -8,6 +8,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.content.pm.Signature; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; @@ -165,6 +166,22 @@ public class Utils { return colorAccent; } + @ColorInt + public static int getColorError(Context context) { + TypedArray ta = context.obtainStyledAttributes(new int[]{android.R.attr.textColorError}); + @ColorInt int colorError = ta.getColor(0, 0); + ta.recycle(); + return colorError; + } + + @ColorInt + public static int getDefaultColor(Context context, int resId) { + final ColorStateList list = + context.getResources().getColorStateList(resId, context.getTheme()); + + return list.getDefaultColor(); + } + /** * Determine whether a package is a "system package", in which case certain things (like * disabling notifications or disabling the package altogether) should be disallowed. diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java index 86514dc5a5cf..5041e0dccc26 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java @@ -28,7 +28,6 @@ import android.content.pm.PackageManager; import android.content.res.TypedArray; import android.os.AsyncTask; import android.os.Bundle; -import android.os.UserManager; import android.provider.Settings; import android.support.v4.widget.DrawerLayout; import android.util.ArraySet; @@ -72,7 +71,6 @@ public class SettingsDrawerActivity extends Activity { private FrameLayout mContentHeaderContainer; private DrawerLayout mDrawerLayout; private boolean mShowingMenu; - private UserManager mUserManager; // Remove below after new IA @Deprecated @@ -108,6 +106,9 @@ public class SettingsDrawerActivity extends Activity { mDrawerLayout = null; return; } + if (!isNavDrawerEnabled()) { + setIsDrawerPresent(false); + } if (!isDashboardFeatureEnabled()) { getDashboardCategories(); } @@ -122,7 +123,6 @@ public class SettingsDrawerActivity extends Activity { } }); - mUserManager = UserManager.get(this); if (DEBUG_TIMING) Log.d(TAG, "onCreate took " + (System.currentTimeMillis() - startTime) + " ms"); } @@ -138,6 +138,15 @@ public class SettingsDrawerActivity extends Activity { } @Override + public boolean onNavigateUp() { + if (!isNavDrawerEnabled()) { + finish(); + return true; + } + return super.onNavigateUp(); + } + + @Override protected void onResume() { super.onResume(); @@ -277,10 +286,13 @@ public class SettingsDrawerActivity extends Activity { } public void showMenuIcon() { - mShowingMenu = true; - getActionBar().setHomeAsUpIndicator(R.drawable.ic_menu); - getActionBar().setHomeActionContentDescription(R.string.content_description_menu_button); getActionBar().setDisplayHomeAsUpEnabled(true); + if (isNavDrawerEnabled()) { + mShowingMenu = true; + getActionBar().setHomeAsUpIndicator(R.drawable.ic_menu); + getActionBar().setHomeActionContentDescription( + R.string.content_description_menu_button); + } } public List<DashboardCategory> getDashboardCategories() { @@ -429,6 +441,11 @@ public class SettingsDrawerActivity extends Activity { return false; } + boolean isNavDrawerEnabled() { + return !isDashboardFeatureEnabled() + || getResources().getBoolean(R.bool.config_enable_nav_drawer); + } + private class PackageReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java index 1e87ea0f18ef..2fd5ec08656b 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java @@ -52,34 +52,34 @@ public class SettingsDrawerActivityTest { } @Test - public void startActivityWithNoExtra_showNoHamburgerMenu() { + public void startActivityWithNoExtra_showNoNavUp() { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); instrumentation.startActivitySync(new Intent(instrumentation.getTargetContext(), TestActivity.class)); - onView(withContentDescription(R.string.content_description_menu_button)) + onView(withContentDescription(com.android.internal.R.string.action_bar_up_description)) .check(doesNotExist()); } @Test - public void startActivityWithExtraToHideMenu_showNoHamburgerMenu() { + public void startActivityWithExtraToHideMenu_showNavUp() { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class) .putExtra(TestActivity.EXTRA_SHOW_MENU, false); instrumentation.startActivitySync(intent); - onView(withContentDescription(R.string.content_description_menu_button)) + onView(withContentDescription(com.android.internal.R.string.action_bar_up_description)) .check(doesNotExist()); } @Test - public void startActivityWithExtraToShowMenu_showHamburgerMenu() { + public void startActivityWithExtraToShowMenu_showNavUp() { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class) .putExtra(TestActivity.EXTRA_SHOW_MENU, true); instrumentation.startActivitySync(intent); - onView(withContentDescription(R.string.content_description_menu_button)) + onView(withContentDescription(com.android.internal.R.string.action_bar_up_description)) .check(matches(isDisplayed())); } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java index 6b01d6687c65..fecc938ad3be 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java @@ -355,9 +355,11 @@ final public class SettingsService extends Binder { final String callPutCommand; if ("system".equals(table)) { callPutCommand = Settings.CALL_METHOD_PUT_SYSTEM; - makeDefault = false; - getOutPrintWriter().println("Ignored makeDefault - " - + "doesn't apply to system settings"); + if (makeDefault) { + getOutPrintWriter().print("Ignored makeDefault - " + + "doesn't apply to system settings"); + makeDefault = false; + } } else if ("secure".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_SECURE; else if ("global".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_GLOBAL; else { diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java index 62d3ce43474f..eab47223bee1 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java @@ -14,18 +14,27 @@ package com.android.systemui.plugins; +import android.app.Notification; +import android.app.Notification.Action; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; +import android.content.res.Resources; import android.net.Uri; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.UserHandle; import android.util.Log; import android.view.LayoutInflater; @@ -260,10 +269,9 @@ public class PluginInstanceManager<T extends Plugin> { String pkg = component.getPackageName(); String cls = component.getClassName(); try { - PackageManager pm = mPm; - ApplicationInfo info = pm.getApplicationInfo(pkg, 0); + ApplicationInfo info = mPm.getApplicationInfo(pkg, 0); // TODO: This probably isn't needed given that we don't have IGNORE_SECURITY on - if (pm.checkPermission(PLUGIN_PERMISSION, pkg) + if (mPm.checkPermission(PLUGIN_PERMISSION, pkg) != PackageManager.PERMISSION_GRANTED) { Log.d(TAG, "Plugin doesn't have permission: " + pkg); return null; @@ -275,6 +283,44 @@ public class PluginInstanceManager<T extends Plugin> { Class<?> pluginClass = Class.forName(cls, true, classLoader); T plugin = (T) pluginClass.newInstance(); if (plugin.getVersion() != mVersion) { + final int id = mContext.getResources().getIdentifier("notification_plugin", + "id", mContext.getPackageName()); + final int icon = mContext.getResources().getIdentifier("tuner", "drawable", + mContext.getPackageName()); + final int color = Resources.getSystem().getIdentifier( + "system_notification_accent_color", "color", "android"); + final Notification.Builder nb = new Notification.Builder(mContext) + .setStyle(new Notification.BigTextStyle()) + .setSmallIcon(icon) + .setWhen(0) + .setShowWhen(false) + .setPriority(Notification.PRIORITY_MAX) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setColor(mContext.getColor(color)); + String label = cls; + try { + label = mPm.getServiceInfo(component, 0).loadLabel(mPm).toString(); + } catch (NameNotFoundException e) { + } + if (plugin.getVersion() < mVersion) { + // Localization not required as this will never ever appear in a user build. + nb.setContentTitle("Plugin \"" + label + "\" is too old") + .setContentText("Contact plugin developer to get an updated" + + " version.\nPlugin version: " + plugin.getVersion() + + "\nSystem version: " + mVersion); + } else { + // Localization not required as this will never ever appear in a user build. + nb.setContentTitle("Plugin \"" + label + "\" is too new") + .setContentText("Check to see if an OTA is available.\n" + + "Plugin version: " + plugin.getVersion() + + "\nSystem version: " + mVersion); + } + Intent i = new Intent(PluginManager.DISABLE_PLUGIN).setData( + Uri.parse("package://" + component.flattenToString())); + PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, 0); + nb.addAction(new Action.Builder(null, "Disable plugin", pi).build()); + mContext.getSystemService(NotificationManager.class) + .notifyAsUser(cls, id, nb.build(), UserHandle.ALL); // TODO: Warn user. Log.w(TAG, "Plugin has invalid interface version " + plugin.getVersion() + ", expected " + mVersion); diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java index 60cf3122966a..c3de09250da4 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java @@ -14,11 +14,14 @@ package com.android.systemui.plugins; +import android.app.NotificationManager; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.net.Uri; import android.os.Build; @@ -42,6 +45,8 @@ public class PluginManager extends BroadcastReceiver { public static final String PLUGIN_CHANGED = "com.android.systemui.action.PLUGIN_CHANGED"; + static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN"; + private static PluginManager sInstance; private final HandlerThread mBackgroundThread; @@ -112,6 +117,7 @@ public class PluginManager extends BroadcastReceiver { filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(PLUGIN_CHANGED); + filter.addAction(DISABLE_PLUGIN); filter.addDataScheme("package"); mContext.registerReceiver(this, filter); filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED); @@ -128,6 +134,17 @@ public class PluginManager extends BroadcastReceiver { for (PluginInstanceManager manager : mPluginMap.values()) { manager.loadAll(); } + } else if (DISABLE_PLUGIN.equals(intent.getAction())) { + Uri uri = intent.getData(); + ComponentName component = ComponentName.unflattenFromString( + uri.toString().substring(10)); + mContext.getPackageManager().setComponentEnabledSetting(component, + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP); + int id = mContext.getResources().getIdentifier("notification_plugin", "id", + mContext.getPackageName()); + mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(), + id); } else { Uri data = intent.getData(); String pkg = data.getEncodedSchemeSpecificPart(); diff --git a/packages/SystemUI/res/color/data_usage_graph_track.xml b/packages/SystemUI/res/color/data_usage_graph_track.xml new file mode 100644 index 000000000000..55c4d2f23937 --- /dev/null +++ b/packages/SystemUI/res/color/data_usage_graph_track.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 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:alpha="0.2" android:color="?android:attr/colorForeground" /> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/color/data_usage_graph_warning.xml b/packages/SystemUI/res/color/data_usage_graph_warning.xml new file mode 100644 index 000000000000..15944c3a2a07 --- /dev/null +++ b/packages/SystemUI/res/color/data_usage_graph_warning.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 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:color="?android:attr/colorForeground" /> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable-nodpi/tuner.xml b/packages/SystemUI/res/drawable-nodpi/tuner.xml index 0596aa4ed14f..c0e51a411b00 100644 --- a/packages/SystemUI/res/drawable-nodpi/tuner.xml +++ b/packages/SystemUI/res/drawable-nodpi/tuner.xml @@ -17,7 +17,8 @@ android:width="24.0dp" android:height="24.0dp" android:viewportWidth="24.0" - android:viewportHeight="24.0"> + android:viewportHeight="24.0" + android:tint="?android:attr/colorControlNormal"> <path android:fillColor="#FFFFFFFF" android:pathData="M22.7,19.0l-9.1,-9.1c0.9,-2.0 0.4,-5.0 -1.5,-6.9 -2.0,-2.0 -5.0,-2.4 -7.4,-1.3L9.0,6.0 6.0,9.0 1.6,4.7C0.4,7.0 0.9,10.1 2.9,12.1c1.9,1.9 4.6,2.4 6.9,1.5l9.1,9.1c0.4,0.4 1.0,0.4 1.4,0.0l2.3,-2.3c0.5,-0.4 0.5,-1.0 0.1,-1.4z"/> diff --git a/packages/SystemUI/res/drawable/ic_device_thermostat_24.xml b/packages/SystemUI/res/drawable/ic_device_thermostat_24.xml new file mode 100644 index 000000000000..1972f6e4427c --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_device_thermostat_24.xml @@ -0,0 +1,25 @@ +<!-- +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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M15,13L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v8c-1.21,0.91 -2,2.37 -2,4 0,2.76 2.24,5 5,5s5,-2.24 5,-5c0,-1.63 -0.79,-3.09 -2,-4zM11,5c0,-0.55 0.45,-1 1,-1s1,0.45 1,1h-1v1h1v2h-1v1h1v2h-2L11,5z"/> +</vector> diff --git a/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml b/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml index efdfae75c1ff..e85b76d3103f 100644 --- a/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml +++ b/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml @@ -67,7 +67,6 @@ android:paddingTop="3dp" android:drawablePadding="8dp" android:drawableStart="@drawable/ic_access_alarms_small" - android:textColor="?android:attr/textColorSecondary" android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date" android:gravity="top" android:background="?android:attr/selectableItemBackground" diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml index 6673d6e8255f..53acb9f14ad9 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml @@ -72,19 +72,20 @@ android:clipChildren="false" android:clipToPadding="false"> - <com.android.systemui.statusbar.phone.SettingsButton android:id="@+id/settings_button" + <com.android.systemui.statusbar.phone.SettingsButton + android:id="@+id/settings_button" style="@android:style/Widget.Material.Button.Borderless" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/ripple_drawable" android:src="@drawable/ic_settings_20dp" android:contentDescription="@string/accessibility_quick_settings_settings" /> - <com.android.systemui.statusbar.AlphaOptimizedImageView android:id="@+id/tuner_icon" + <com.android.systemui.statusbar.AlphaOptimizedImageView + android:id="@+id/tuner_icon" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingStart="36dp" - android:tint="#4DFFFFFF" - android:tintMode="src_in" + android:alpha="0.3" android:visibility="invisible" android:src="@drawable/tuner" /> diff --git a/packages/SystemUI/res/layout/status_bar_alarm_group.xml b/packages/SystemUI/res/layout/status_bar_alarm_group.xml index 03585b854898..a02e9af674fa 100644 --- a/packages/SystemUI/res/layout/status_bar_alarm_group.xml +++ b/packages/SystemUI/res/layout/status_bar_alarm_group.xml @@ -65,7 +65,6 @@ android:paddingTop="3dp" android:drawablePadding="8dp" android:drawableStart="@drawable/ic_access_alarms_small" - android:textColor="?android:attr/textColorSecondary" android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date" android:gravity="top" android:background="?android:attr/selectableItemBackground" diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index ba8c64410443..b18b6acb6a35 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -31,15 +31,11 @@ <color name="batterymeter_bolt_color">#FFFFFFFF</color> <color name="qs_batterymeter_frame_color">#FF404040</color> <color name="system_warning_color">@*android:color/system_error</color> - <color name="qs_text">#FFFFFFFF</color> <color name="qs_tile_divider">#29ffffff</color><!-- 16% white --> <color name="qs_subhead">#99FFFFFF</color><!-- 60% white --> <color name="qs_detail_button">@*android:color/quaternary_device_default_settings</color> <color name="qs_detail_button_white">#B3FFFFFF</color><!-- 70% white --> <color name="qs_detail_transition">#66FFFFFF</color> - <color name="data_usage_secondary">#99FFFFFF</color><!-- 60% white --> - <color name="data_usage_graph_track">#33FFFFFF</color><!-- 20% white --> - <color name="data_usage_graph_warning">#FFFFFFFF</color> <color name="status_bar_clock_color">#FFFFFFFF</color> <color name="qs_user_detail_icon_muted">#FFFFFFFF</color> <!-- not so muted after all --> <color name="qs_tile_disabled_color">#9E9E9E</color> <!-- 38% black --> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 0f5d37ea3691..c025f932adf8 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -290,4 +290,6 @@ <bool name="quick_settings_show_full_alarm">false</bool> + <bool name="config_showTemperatureWarning">false</bool> + </resources> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 2a895f98eefd..56cd8c7c97a3 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -53,6 +53,8 @@ <item type="id" name="notification_screenshot"/> <item type="id" name="notification_hidden"/> <item type="id" name="notification_volumeui"/> + <item type="id" name="notification_temperature"/> + <item type="id" name="notification_plugin"/> <item type="id" name="transformation_start_x_tag"/> <item type="id" name="transformation_start_y_tag"/> <item type="id" name="transformation_start_scale_x_tag"/> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 194653ba15ce..d7033b26f563 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1766,4 +1766,12 @@ <!-- Tuner string --> <string name="default_theme" translatable="false">Default</string> + <!-- Title for notification (and dialog) that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=30] --> + <string name="high_temp_title">Phone is getting warm</string> + <!-- Message body for notification that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=70] --> + <string name="high_temp_notif_message">Some features limited while phone cools down</string> + <!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=300] --> + <string name="high_temp_dialog_message">Your phone will automatically try to cool down. You can still use your phone, but it may run slower.\n\nOnce your phone has cooled down, it will run normally.</string> + + </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 614472a7fd01..a9c858c0e721 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -117,13 +117,13 @@ <style name="TextAppearance.StatusBar.Expanded.Date"> <item name="android:textSize">@dimen/qs_date_collapsed_size</item> <item name="android:textStyle">normal</item> - <item name="android:textColor">#b2ffffff</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> </style> <style name="TextAppearance.StatusBar.Expanded.AboveDateTime"> <item name="android:textSize">@dimen/qs_emergency_calls_only_text_size</item> <item name="android:textStyle">normal</item> - <item name="android:textColor">#66ffffff</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> </style> <style name="TextAppearance.StatusBar.Expanded.EmergencyCallsOnly" @@ -207,7 +207,7 @@ </style> <style name="TextAppearance.QS.DataUsage.Secondary"> - <item name="android:textColor">@color/data_usage_secondary</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> </style> <style name="TextAppearance.QS.TileLabel"> diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java index 8a500c3219f0..848fe9d5a9df 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java @@ -50,7 +50,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { private static final String TAG = PowerUI.TAG + ".Notification"; private static final boolean DEBUG = PowerUI.DEBUG; - private static final String TAG_NOTIFICATION = "low_battery"; + private static final String TAG_NOTIFICATION_BATTERY = "low_battery"; + private static final String TAG_NOTIFICATION_TEMPERATURE = "high_temp"; private static final int SHOWING_NOTHING = 0; private static final int SHOWING_WARNING = 1; @@ -65,6 +66,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { private static final String ACTION_SHOW_BATTERY_SETTINGS = "PNW.batterySettings"; private static final String ACTION_START_SAVER = "PNW.startSaver"; private static final String ACTION_DISMISSED_WARNING = "PNW.dismissedWarning"; + private static final String ACTION_CLICKED_TEMP_WARNING = "PNW.clickedTempWarning"; + private static final String ACTION_DISMISSED_TEMP_WARNING = "PNW.dismissedTempWarning"; private static final AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) @@ -89,6 +92,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { private boolean mPlaySound; private boolean mInvalidCharger; private SystemUIDialog mSaverConfirmation; + private boolean mTempWarning; + private SystemUIDialog mHighTempDialog; public PowerNotificationWarnings(Context context, NotificationManager notificationManager, PhoneStatusBar phoneStatusBar) { @@ -105,6 +110,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { pw.print("mInvalidCharger="); pw.println(mInvalidCharger); pw.print("mShowing="); pw.println(SHOWING_STRINGS[mShowing]); pw.print("mSaverConfirmation="); pw.println(mSaverConfirmation != null ? "not null" : null); + pw.print("mTempWarning="); pw.println(mTempWarning); + pw.print("mHighTempDialog="); pw.println(mHighTempDialog != null ? "not null" : null); } @Override @@ -129,7 +136,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { showWarningNotification(); mShowing = SHOWING_WARNING; } else { - mNoMan.cancelAsUser(TAG_NOTIFICATION, R.id.notification_power, UserHandle.ALL); + mNoMan.cancelAsUser(TAG_NOTIFICATION_BATTERY, R.id.notification_power, UserHandle.ALL); mShowing = SHOWING_NOTHING; } } @@ -148,7 +155,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { com.android.internal.R.color.system_notification_accent_color)); SystemUI.overrideNotificationAppName(mContext, nb); final Notification n = nb.build(); - mNoMan.notifyAsUser(TAG_NOTIFICATION, R.id.notification_power, n, UserHandle.ALL); + mNoMan.notifyAsUser(TAG_NOTIFICATION_BATTERY, R.id.notification_power, n, UserHandle.ALL); } private void showWarningNotification() { @@ -178,12 +185,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { mPlaySound = false; } SystemUI.overrideNotificationAppName(mContext, nb); - mNoMan.notifyAsUser(TAG_NOTIFICATION, R.id.notification_power, nb.build(), UserHandle.ALL); - } - - private PendingIntent pendingActivity(Intent intent) { - return PendingIntent.getActivityAsUser(mContext, - 0, intent, 0, null, UserHandle.CURRENT); + mNoMan.notifyAsUser( + TAG_NOTIFICATION_BATTERY, R.id.notification_power, nb.build(), UserHandle.ALL); } private PendingIntent pendingBroadcast(String action) { @@ -205,6 +208,54 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { } @Override + public void dismissTemperatureWarning() { + if (!mTempWarning) { + return; + } + mTempWarning = false; + mNoMan.cancelAsUser( + TAG_NOTIFICATION_TEMPERATURE, R.id.notification_temperature, UserHandle.ALL); + } + + @Override + public void showTemperatureWarning() { + if (mTempWarning) { + return; + } + mTempWarning = true; + final Notification.Builder nb = new Notification.Builder(mContext) + .setSmallIcon(R.drawable.ic_device_thermostat_24) + .setWhen(0) + .setShowWhen(false) + .setContentTitle(mContext.getString(R.string.high_temp_title)) + .setContentText(mContext.getString(R.string.high_temp_notif_message)) + .setPriority(Notification.PRIORITY_HIGH) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .setContentIntent(pendingBroadcast(ACTION_CLICKED_TEMP_WARNING)) + .setDeleteIntent(pendingBroadcast(ACTION_DISMISSED_TEMP_WARNING)) + .setColor(mContext.getColor( + com.android.internal.R.color.battery_saver_mode_color)); + SystemUI.overrideNotificationAppName(mContext, nb); + final Notification n = nb.build(); + mNoMan.notifyAsUser( + TAG_NOTIFICATION_TEMPERATURE, R.id.notification_temperature, n, UserHandle.ALL); + + } + + private void showTemperatureDialog() { + if (mHighTempDialog != null) return; + final SystemUIDialog d = new SystemUIDialog(mContext); + d.setIconAttribute(android.R.attr.alertDialogIcon); + d.setTitle(R.string.high_temp_title); + d.setMessage(R.string.high_temp_dialog_message); + d.setPositiveButton(com.android.internal.R.string.ok, null); + d.setShowForAllUsers(true); + d.setOnDismissListener(dialog -> mHighTempDialog = null); + d.show(); + mHighTempDialog = d; + } + + @Override public void updateLowBatteryWarning() { updateNotification(); } @@ -317,6 +368,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { filter.addAction(ACTION_SHOW_BATTERY_SETTINGS); filter.addAction(ACTION_START_SAVER); filter.addAction(ACTION_DISMISSED_WARNING); + filter.addAction(ACTION_CLICKED_TEMP_WARNING); + filter.addAction(ACTION_DISMISSED_TEMP_WARNING); mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, android.Manifest.permission.STATUS_BAR_SERVICE, mHandler); } @@ -333,6 +386,11 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { showStartSaverConfirmation(); } else if (action.equals(ACTION_DISMISSED_WARNING)) { dismissLowBatteryWarning(); + } else if (ACTION_CLICKED_TEMP_WARNING.equals(action)) { + dismissTemperatureWarning(); + showTemperatureDialog(); + } else if (ACTION_DISMISSED_TEMP_WARNING.equals(action)) { + dismissTemperatureWarning(); } } } diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java index b651f2d99523..d4bb994010a3 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -25,12 +25,15 @@ import android.content.IntentFilter; import android.database.ContentObserver; import android.os.BatteryManager; import android.os.Handler; +import android.os.HardwarePropertiesManager; import android.os.PowerManager; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; +import android.text.format.DateUtils; import android.util.Log; import android.util.Slog; +import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.statusbar.phone.PhoneStatusBar; import java.io.FileDescriptor; @@ -40,11 +43,13 @@ import java.util.Arrays; public class PowerUI extends SystemUI { static final String TAG = "PowerUI"; static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final long TEMPERATURE_INTERVAL = 30 * DateUtils.SECOND_IN_MILLIS; private final Handler mHandler = new Handler(); private final Receiver mReceiver = new Receiver(); private PowerManager mPowerManager; + private HardwarePropertiesManager mHardwarePropertiesManager; private WarningsUI mWarnings; private int mBatteryLevel = 100; private int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN; @@ -56,8 +61,12 @@ public class PowerUI extends SystemUI { private long mScreenOffTime = -1; + private float mThrottlingTemp; + public void start() { mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mHardwarePropertiesManager = (HardwarePropertiesManager) + mContext.getSystemService(Context.HARDWARE_PROPERTIES_SERVICE); mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime(); mWarnings = new PowerNotificationWarnings( mContext, @@ -76,6 +85,8 @@ public class PowerUI extends SystemUI { false, obs, UserHandle.USER_ALL); updateBatteryWarningLevels(); mReceiver.init(); + + initTemperatureWarning(); } void updateBatteryWarningLevels() { @@ -136,8 +147,6 @@ public class PowerUI extends SystemUI { filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_USER_SWITCHED); - filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING); - filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); mContext.registerReceiver(this, filter, null, mHandler); } @@ -211,6 +220,53 @@ public class PowerUI extends SystemUI { } }; + private void initTemperatureWarning() { + if (!mContext.getResources().getBoolean(R.bool.config_showTemperatureWarning)) { + return; + } + + // Get the throttling temperature. No need to check if we're not throttling. + float[] throttlingTemps = mHardwarePropertiesManager.getDeviceTemperatures( + HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN, + HardwarePropertiesManager.TEMPERATURE_THROTTLING); + if (throttlingTemps == null + || throttlingTemps.length == 0 + || throttlingTemps[0] == HardwarePropertiesManager.UNDEFINED_TEMPERATURE) { + return; + } + mThrottlingTemp = throttlingTemps[0]; + + // We have passed all of the checks, start checking the temp + updateTemperatureWarning(); + } + + private void updateTemperatureWarning() { + PhoneStatusBar phoneStatusBar = getComponent(PhoneStatusBar.class); + if (phoneStatusBar != null && phoneStatusBar.isDeviceInVrMode()) { + // ensure the warning isn't showing, since VR shows its own warning + mWarnings.dismissTemperatureWarning(); + } else { + float[] temps = mHardwarePropertiesManager.getDeviceTemperatures( + HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN, + HardwarePropertiesManager.TEMPERATURE_CURRENT); + boolean shouldShowTempWarning = false; + for (float temp : temps) { + if (temp >= mThrottlingTemp) { + shouldShowTempWarning = true; + break; + } + } + if (shouldShowTempWarning) { + mWarnings.showTemperatureWarning(); + } else { + mWarnings.dismissTemperatureWarning(); + } + } + + // TODO: skip this when in VR mode since we already get a callback + mHandler.postDelayed(this::updateTemperatureWarning, TEMPERATURE_INTERVAL); + } + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.print("mLowBatteryAlertCloseLevel="); pw.println(mLowBatteryAlertCloseLevel); @@ -237,6 +293,8 @@ public class PowerUI extends SystemUI { Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0)); pw.print("bucket: "); pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel))); + pw.print("mThrottlingTemp="); + pw.println(Float.toString(mThrottlingTemp)); mWarnings.dump(pw); } @@ -248,6 +306,8 @@ public class PowerUI extends SystemUI { void showInvalidChargerWarning(); void updateLowBatteryWarning(); boolean isInvalidChargerWarningShowing(); + void dismissTemperatureWarning(); + void showTemperatureWarning(); void dump(PrintWriter pw); void userSwitched(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java b/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java index 5f23a40ab3b8..047e0d17ad18 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java +++ b/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java @@ -45,10 +45,10 @@ public class DataUsageGraph extends View { public DataUsageGraph(Context context, AttributeSet attrs) { super(context, attrs); final Resources res = context.getResources(); - mTrackColor = context.getColor(R.color.data_usage_graph_track); + mTrackColor = Utils.getDefaultColor(context, R.color.data_usage_graph_track); + mWarningColor = Utils.getDefaultColor(context, R.color.data_usage_graph_warning); mUsageColor = Utils.getColorAccent(context); - mOverlimitColor = context.getColor(R.color.system_warning_color); - mWarningColor = context.getColor(R.color.data_usage_graph_warning); + mOverlimitColor = Utils.getColorError(context); mMarkerWidth = res.getDimensionPixelSize(R.dimen.data_usage_graph_marker_width); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java index 9050b83f4ce0..66e617bd8f75 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java @@ -17,11 +17,15 @@ package com.android.systemui.plugins; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Activity; +import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -34,6 +38,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.HandlerThread; +import android.os.UserHandle; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; @@ -72,7 +77,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { mMockPm = mock(PackageManager.class); mMockListener = mock(PluginListener.class); mMockManager = mock(PluginManager.class); - when(mMockManager.getClassLoader(Mockito.any(), Mockito.any())) + when(mMockManager.getClassLoader(any(), any())) .thenReturn(getClass().getClassLoader()); mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction", mMockListener, true, mHandlerThread.getLooper(), 1, mMockManager, true); @@ -87,8 +92,8 @@ public class PluginInstanceManagerTest extends SysuiTestCase { } @Test - public void testNoPlugins() { - when(mMockPm.queryIntentServices(Mockito.any(), Mockito.anyInt())).thenReturn( + public void testNoPlugins() throws Exception { + when(mMockPm.queryIntentServices(any(), anyInt())).thenReturn( Collections.emptyList()); mPluginInstanceManager.loadAll(); @@ -100,7 +105,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { } @Test - public void testPluginCreate() { + public void testPluginCreate() throws Exception { createPlugin(); // Verify startup lifecycle @@ -110,7 +115,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { } @Test - public void testPluginDestroy() { + public void testPluginDestroy() throws Exception { createPlugin(); // Get into valid created state. mPluginInstanceManager.destroy(); @@ -124,7 +129,9 @@ public class PluginInstanceManagerTest extends SysuiTestCase { } @Test - public void testIncorrectVersion() { + public void testIncorrectVersion() throws Exception { + NotificationManager nm = mock(NotificationManager.class); + mContext.addMockSystemService(Context.NOTIFICATION_SERVICE, nm); setupFakePmQuery(); when(sMockPlugin.getVersion()).thenReturn(2); @@ -136,10 +143,12 @@ public class PluginInstanceManagerTest extends SysuiTestCase { // Plugin shouldn't be connected because it is the wrong version. verify(mMockListener, Mockito.never()).onPluginConnected( ArgumentCaptor.forClass(Plugin.class).capture()); + verify(nm).notifyAsUser(eq(TestPlugin.class.getName()), eq(R.id.notification_plugin), any(), + eq(UserHandle.ALL)); } @Test - public void testReloadOnChange() { + public void testReloadOnChange() throws Exception { createPlugin(); // Get into valid created state. mPluginInstanceManager.onPackageChange("com.android.systemui"); @@ -159,7 +168,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { } @Test - public void testNonDebuggable() { + public void testNonDebuggable() throws Exception { // Create a version that thinks the build is not debuggable. mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction", mMockListener, true, mHandlerThread.getLooper(), 1, mMockManager, false); @@ -176,7 +185,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { } @Test - public void testCheckAndDisable() { + public void testCheckAndDisable() throws Exception { createPlugin(); // Get into valid created state. // Start with an unrelated class. @@ -197,7 +206,7 @@ public class PluginInstanceManagerTest extends SysuiTestCase { } @Test - public void testDisableAll() { + public void testDisableAll() throws Exception { createPlugin(); // Get into valid created state. mPluginInstanceManager.disableAll(); @@ -208,29 +217,26 @@ public class PluginInstanceManagerTest extends SysuiTestCase { ArgumentCaptor.forClass(int.class).capture()); } - private void setupFakePmQuery() { + private void setupFakePmQuery() throws Exception { List<ResolveInfo> list = new ArrayList<>(); ResolveInfo info = new ResolveInfo(); - info.serviceInfo = new ServiceInfo(); + info.serviceInfo = mock(ServiceInfo.class); info.serviceInfo.packageName = "com.android.systemui"; info.serviceInfo.name = TestPlugin.class.getName(); + when(info.serviceInfo.loadLabel(any())).thenReturn("Test Plugin"); list.add(info); - when(mMockPm.queryIntentServices(Mockito.any(), Mockito.anyInt())).thenReturn(list); + when(mMockPm.queryIntentServices(any(), Mockito.anyInt())).thenReturn(list); + when(mMockPm.getServiceInfo(any(), anyInt())).thenReturn(info.serviceInfo); when(mMockPm.checkPermission(Mockito.anyString(), Mockito.anyString())).thenReturn( PackageManager.PERMISSION_GRANTED); - try { - ApplicationInfo appInfo = getContext().getApplicationInfo(); - when(mMockPm.getApplicationInfo(Mockito.anyString(), Mockito.anyInt())).thenReturn( - appInfo); - } catch (NameNotFoundException e) { - // Shouldn't be possible, but if it is, we want to fail. - throw new RuntimeException(e); - } + ApplicationInfo appInfo = getContext().getApplicationInfo(); + when(mMockPm.getApplicationInfo(Mockito.anyString(), Mockito.anyInt())).thenReturn( + appInfo); } - private void createPlugin() { + private void createPlugin() throws Exception { setupFakePmQuery(); mPluginInstanceManager.loadAll(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java index 4b1827d51a34..63b1817a1f78 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java @@ -13,10 +13,17 @@ */ package com.android.systemui.plugins; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.NotificationManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; @@ -113,6 +120,24 @@ public class PluginManagerTest extends SysuiTestCase { ArgumentCaptor.forClass(Throwable.class).capture()); } + @Test + public void testDisableIntent() { + NotificationManager nm = mock(NotificationManager.class); + PackageManager pm = mock(PackageManager.class); + mContext.addMockSystemService(Context.NOTIFICATION_SERVICE, nm); + mContext.setMockPackageManager(pm); + + ComponentName testComponent = new ComponentName(getContext().getPackageName(), + PluginManagerTest.class.getName()); + Intent intent = new Intent(PluginManager.DISABLE_PLUGIN); + intent.setData(Uri.parse("package://" + testComponent.flattenToString())); + mPluginManager.onReceive(mContext, intent); + verify(nm).cancel(eq(testComponent.getClassName()), eq(R.id.notification_plugin)); + verify(pm).setComponentEnabledSetting(eq(testComponent), + eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED), + eq(PackageManager.DONT_KILL_APP)); + } + private void resetExceptionHandler() { mPluginExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); // Set back the real exception handler so the test can crash if it wants to. diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java index 5c87fb01e23c..7070961e39b0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java @@ -117,4 +117,18 @@ public class PowerNotificationWarningsTest extends SysuiTestCase { .notifyAsUser(anyString(), anyInt(), captor.capture(), any()); assertNotEqual(null, captor.getValue().sound); } + + @Test + public void testShowTemperatureWarning_NotifyAsUser() { + mPowerNotificationWarnings.showTemperatureWarning(); + verify(mMockNotificationManager, times(1)) + .notifyAsUser(anyString(), anyInt(), any(), any()); + } + + @Test + public void testDismissTemperatureWarning_CancelAsUser() { + mPowerNotificationWarnings.showTemperatureWarning(); + mPowerNotificationWarnings.dismissTemperatureWarning(); + verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(), anyInt(), any()); + } } 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 a95280641aa1..710f88af46bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java @@ -23,6 +23,7 @@ import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.Handler; import android.os.IBinder; @@ -42,6 +43,7 @@ public class TestableContext extends ContextWrapper { private ArrayMap<ComponentName, IBinder> mMockServices; private ArrayMap<ServiceConnection, ComponentName> mActiveServices; + private PackageManager mMockPackageManager; private Tracker mReceiver; private Tracker mService; private Tracker mComponent; @@ -59,6 +61,18 @@ public class TestableContext extends ContextWrapper { mComponent = test.getTracker("component"); } + public void setMockPackageManager(PackageManager mock) { + mMockPackageManager = mock; + } + + @Override + public PackageManager getPackageManager() { + if (mMockPackageManager != null) { + return mMockPackageManager; + } + return super.getPackageManager(); + } + @Override public Resources getResources() { return super.getResources(); diff --git a/services/Android.mk b/services/Android.mk index 291198303548..0d57efe27833 100644 --- a/services/Android.mk +++ b/services/Android.mk @@ -24,6 +24,7 @@ services := \ appwidget \ autofill \ backup \ + coverage\ devicepolicy \ midi \ net \ @@ -37,6 +38,10 @@ services := \ # The convention is to name each service module 'services.$(module_name)' LOCAL_STATIC_JAVA_LIBRARIES := $(addprefix services.,$(services)) +ifeq ($(EMMA_INSTRUMENT_FRAMEWORK),true) +LOCAL_EMMA_INSTRUMENT := true +endif + include $(BUILD_JAVA_LIBRARY) # native library diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 3419963aaebf..e7f1d16fcc61 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -2045,6 +2045,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } writer.flush(); + if (args.length == 0) { + // Add arg to produce output + args = new String[1]; + args[0] = "--print"; + } } if (mBluetoothBinder == null) { diff --git a/services/core/java/com/android/server/HardwarePropertiesManagerService.java b/services/core/java/com/android/server/HardwarePropertiesManagerService.java index 23cf64a031af..36a16cd0a18b 100644 --- a/services/core/java/com/android/server/HardwarePropertiesManagerService.java +++ b/services/core/java/com/android/server/HardwarePropertiesManagerService.java @@ -16,6 +16,8 @@ package com.android.server; +import android.Manifest; +import android.app.ActivityManager; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.pm.PackageManager; @@ -80,8 +82,9 @@ public class HardwarePropertiesManagerService extends IHardwarePropertiesManager * * @param callingPackage The calling package name. * - * @throws SecurityException if something other than the profile or device owner, or the - * current VR service tries to retrieve information provided by this service. + * @throws SecurityException if something other than the profile or device owner, the + * current VR service, or a caller holding the {@link Manifest.permission#DEVICE_POWER} + * permission tries to retrieve information provided by this service. */ private void enforceHardwarePropertiesRetrievalAllowed(String callingPackage) throws SecurityException { @@ -100,9 +103,11 @@ public class HardwarePropertiesManagerService extends IHardwarePropertiesManager final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class); final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); if (!dpm.isDeviceOwnerApp(callingPackage) && !dpm.isProfileOwnerApp(callingPackage) - && !vrService.isCurrentVrListener(callingPackage, userId)) { - throw new SecurityException("The caller is not a device or profile owner or bound " - + "VrListenerService."); + && !vrService.isCurrentVrListener(callingPackage, userId) + && mContext.checkCallingOrSelfPermission(Manifest.permission.DEVICE_POWER) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("The caller is not a device or profile owner, bound " + + "VrListenerService, or holding the DEVICE_POWER permission."); } } } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 235325b8b259..028c57117824 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -2177,6 +2177,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer // display because it no longer contains any tasks. mAllowDockedStackResize = false; } + final ActivityStack fullscreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID); + final boolean isFullscreenStackVisible = fullscreenStack != null && + fullscreenStack.getStackVisibilityLocked(null) == STACK_VISIBLE; final ArrayList<TaskRecord> tasks = stack.getAllTasks(); final int size = tasks.size(); if (onTop) { @@ -2186,9 +2189,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer // Update the return-to to reflect where the pinned stack task was moved // from so that we retain the stack that was previously visible if the // pinned stack is recreated. See moveActivityToPinnedStackLocked(). - final int focusedStackId = getFocusedStack().getStackId(); - task.setTaskToReturnTo(focusedStackId == HOME_STACK_ID || !onTop - ? HOME_ACTIVITY_TYPE : APPLICATION_ACTIVITY_TYPE); + task.setTaskToReturnTo(isFullscreenStackVisible && onTop ? + APPLICATION_ACTIVITY_TYPE : HOME_ACTIVITY_TYPE); } moveTaskToStackLocked(tasks.get(i).taskId, FULLSCREEN_WORKSPACE_STACK_ID, onTop, onTop /*forceFocus*/, diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index e91cce11f7df..f5b866981993 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -126,8 +126,6 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_NETWORK_RESET, UserManager.DISALLOW_FACTORY_RESET, UserManager.DISALLOW_ADD_USER, - UserManager.DISALLOW_ADD_MANAGED_PROFILE, - UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, @@ -261,6 +259,7 @@ public class UserRestrictionsUtils { /** * Returns the user restrictions that default to {@code true} for device owners. + * These user restrictions are local, though. ie only for the device owner's user id. */ public static @NonNull Set<String> getDefaultEnabledForDeviceOwner() { return DEFAULT_ENABLED_FOR_DEVICE_OWNERS; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ff841b147d9c..d86c4dab54fb 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -2498,19 +2498,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } private void addChild(TaskStack stack, boolean toTop) { - int addIndex = toTop ? mChildren.size() : 0; - - if (toTop - && mService.isStackVisibleLocked(PINNED_STACK_ID) - && stack.mStackId != PINNED_STACK_ID) { - // The pinned stack is always the top most stack (always-on-top) when it is visible. - // So, stack is moved just below the pinned stack. - addIndex--; - TaskStack topStack = mChildren.get(addIndex); - if (topStack.mStackId != PINNED_STACK_ID) { - throw new IllegalStateException("Pinned stack isn't top stack??? " + mChildren); - } - } + final int addIndex = findPositionForStack(toTop ? mChildren.size() : 0, stack, + true /* adding */); addChild(stack, addIndex); setLayoutNeeded(); } @@ -2528,7 +2517,45 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return; } - super.positionChildAt(position, child, includingParents); + final int targetPosition = findPositionForStack(position, child, false /* adding */); + super.positionChildAt(targetPosition, child, includingParents); + + setLayoutNeeded(); + } + + /** + * When stack is added or repositioned, find a proper position for it. + * This will make sure that pinned stack always stays on top. + * @param requestedPosition Position requested by caller. + * @param stack Stack to be added or positioned. + * @param adding Flag indicates whether we're adding a new stack or positioning an existing. + * @return The proper position for the stack. + */ + private int findPositionForStack(int requestedPosition, TaskStack stack, boolean adding) { + final int topChildPosition = mChildren.size() - 1; + boolean toTop = requestedPosition == POSITION_TOP; + toTop |= adding ? requestedPosition >= topChildPosition + 1 + : requestedPosition >= topChildPosition; + int targetPosition = requestedPosition; + + if (toTop + && mService.isStackVisibleLocked(PINNED_STACK_ID) + && stack.mStackId != PINNED_STACK_ID) { + // The pinned stack is always the top most stack (always-on-top) when it is visible. + TaskStack topStack = mChildren.get(topChildPosition); + if (topStack.mStackId != PINNED_STACK_ID) { + throw new IllegalStateException("Pinned stack isn't top stack??? " + mChildren); + } + + // So, stack is moved just below the pinned stack. + // When we're adding a new stack the target is the current pinned stack position. + // When we're positioning an existing stack the target is the position below pinned + // stack, because WindowContainer#positionAt() first removes element and then adds it + // to specified place. + targetPosition = adding ? topChildPosition : topChildPosition - 1; + } + + return targetPosition; } @Override diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 984cf55de0e7..c9bf4fa55efd 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -434,7 +434,7 @@ class WindowContainer<E extends WindowContainer> implements Comparable<WindowCon // TODO: Will this be more correct if it checks the visibility of its parents? // It depends...For example, Tasks and Stacks are only visible if there children are visible // but, WindowState are not visible if there parent are not visible. Maybe have the - // container specify which direction to treverse for for visibility? + // container specify which direction to traverse for visibility? for (int i = mChildren.size() - 1; i >= 0; --i) { final WindowContainer wc = mChildren.get(i); if (wc.isVisible()) { diff --git a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp index c42141ada543..17a6c297bd8f 100644 --- a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp +++ b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp @@ -37,6 +37,8 @@ using hardware::thermal::V1_0::IThermal; using hardware::thermal::V1_0::Temperature; using hardware::thermal::V1_0::ThermalStatus; using hardware::thermal::V1_0::ThermalStatusCode; +template<typename T> +using Return = hardware::Return<T>; // --------------------------------------------------------------------------- @@ -76,7 +78,7 @@ static jfloatArray nativeGetFanSpeeds(JNIEnv *env, jclass /* clazz */) { } hidl_vec<CoolingDevice> list; - Status status = gThermalModule->getCoolingDevices( + Return<void> ret = gThermalModule->getCoolingDevices( [&list](ThermalStatus status, hidl_vec<CoolingDevice> devices) { if (status.code == ThermalStatusCode::SUCCESS) { list = std::move(devices); @@ -84,10 +86,10 @@ static jfloatArray nativeGetFanSpeeds(JNIEnv *env, jclass /* clazz */) { ALOGE("Couldn't get fan speeds because of HAL error: %s", status.debugMessage.c_str()); } - }).getStatus(); + }); - if (!status.isOk()) { - ALOGE("getCoolingDevices failed status: %d", status.exceptionCode()); + if (!ret.isOk()) { + ALOGE("getCoolingDevices failed status: %s", ret.description().c_str()); } float values[list.size()]; @@ -106,7 +108,7 @@ static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */, return env->NewFloatArray(0); } hidl_vec<Temperature> list; - Status status = gThermalModule->getTemperatures( + Return<void> ret = gThermalModule->getTemperatures( [&list](ThermalStatus status, hidl_vec<Temperature> temperatures) { if (status.code == ThermalStatusCode::SUCCESS) { list = std::move(temperatures); @@ -114,10 +116,10 @@ static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */, ALOGE("Couldn't get temperatures because of HAL error: %s", status.debugMessage.c_str()); } - }).getStatus(); + }); - if (!status.isOk()) { - ALOGE("getDeviceTemperatures failed status: %d", status.exceptionCode()); + if (!ret.isOk()) { + ALOGE("getDeviceTemperatures failed status: %s", ret.description().c_str()); } jfloat values[list.size()]; @@ -151,7 +153,7 @@ static jobjectArray nativeGetCpuUsages(JNIEnv *env, jclass /* clazz */) { return env->NewObjectArray(0, gCpuUsageInfoClassInfo.clazz, nullptr); } hidl_vec<CpuUsage> list; - Status status = gThermalModule->getCpuUsages( + Return<void> ret = gThermalModule->getCpuUsages( [&list](ThermalStatus status, hidl_vec<CpuUsage> cpuUsages) { if (status.code == ThermalStatusCode::SUCCESS) { list = std::move(cpuUsages); @@ -159,10 +161,10 @@ static jobjectArray nativeGetCpuUsages(JNIEnv *env, jclass /* clazz */) { ALOGE("Couldn't get CPU usages because of HAL error: %s", status.debugMessage.c_str()); } - }).getStatus(); + }); - if (!status.isOk()) { - ALOGE("getCpuUsages failed status: %d", status.exceptionCode()); + if (!ret.isOk()) { + ALOGE("getCpuUsages failed status: %s", ret.description().c_str()); } jobjectArray cpuUsages = env->NewObjectArray(list.size(), gCpuUsageInfoClassInfo.clazz, diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp index 0c5729e9b32c..7790d15719e9 100644 --- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp +++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp @@ -185,14 +185,14 @@ HdmiCecController::HdmiCecController(sp<IHdmiCec> hdmiCec, mLooper(looper) { mHdmiCecCallback = new HdmiCecCallback(this); Return<void> ret = mHdmiCec->setCallback(mHdmiCecCallback); - if (!ret.getStatus().isOk()) { + if (!ret.isOk()) { ALOGE("Failed to set a cec callback."); } } HdmiCecController::~HdmiCecController() { Return<void> ret = mHdmiCec->setCallback(nullptr); - if (!ret.getStatus().isOk()) { + if (!ret.isOk()) { ALOGE("Failed to set a cec callback."); } } @@ -200,7 +200,7 @@ HdmiCecController::~HdmiCecController() { int HdmiCecController::sendMessage(const CecMessage& message) { // TODO: propagate send_message's return value. Return<SendMessageResult> ret = mHdmiCec->sendMessage(message); - if (!ret.getStatus().isOk()) { + if (!ret.isOk()) { ALOGE("Failed to send CEC message."); return static_cast<int>(SendMessageResult::FAIL); } @@ -209,7 +209,7 @@ int HdmiCecController::sendMessage(const CecMessage& message) { int HdmiCecController::addLogicalAddress(CecLogicalAddress address) { Return<Result> ret = mHdmiCec->addLogicalAddress(address); - if (!ret.getStatus().isOk()) { + if (!ret.isOk()) { ALOGE("Failed to add a logical address."); return static_cast<int>(Result::FAILURE_UNKNOWN); } @@ -218,7 +218,7 @@ int HdmiCecController::addLogicalAddress(CecLogicalAddress address) { void HdmiCecController::clearLogicaladdress() { Return<void> ret = mHdmiCec->clearLogicalAddress(); - if (!ret.getStatus().isOk()) { + if (!ret.isOk()) { ALOGE("Failed to clear logical address."); } } @@ -230,7 +230,7 @@ int HdmiCecController::getPhysicalAddress() { result = res; addr = paddr; }); - if (!ret.getStatus().isOk()) { + if (!ret.isOk()) { ALOGE("Failed to get physical address."); return INVALID_PHYSICAL_ADDRESS; } @@ -239,7 +239,7 @@ int HdmiCecController::getPhysicalAddress() { int HdmiCecController::getVersion() { Return<int32_t> ret = mHdmiCec->getCecVersion(); - if (!ret.getStatus().isOk()) { + if (!ret.isOk()) { ALOGE("Failed to get cec version."); } return ret; @@ -247,7 +247,7 @@ int HdmiCecController::getVersion() { uint32_t HdmiCecController::getVendorId() { Return<uint32_t> ret = mHdmiCec->getVendorId(); - if (!ret.getStatus().isOk()) { + if (!ret.isOk()) { ALOGE("Failed to get vendor id."); } return ret; @@ -267,7 +267,7 @@ jobjectArray HdmiCecController::getPortInfos() { Return<void> ret = mHdmiCec->getPortInfo([&ports](hidl_vec<HdmiPortInfo> list) { ports = list; }); - if (!ret.getStatus().isOk()) { + if (!ret.isOk()) { ALOGE("Failed to get port information."); return NULL; } @@ -287,14 +287,14 @@ jobjectArray HdmiCecController::getPortInfos() { void HdmiCecController::setOption(OptionKey key, bool enabled) { Return<void> ret = mHdmiCec->setOption(key, enabled); - if (!ret.getStatus().isOk()) { + if (!ret.isOk()) { ALOGE("Failed to set option."); } } void HdmiCecController::setLanguage(hidl_string language) { Return<void> ret = mHdmiCec->setLanguage(language); - if (!ret.getStatus().isOk()) { + if (!ret.isOk()) { ALOGE("Failed to set language."); } } @@ -302,7 +302,7 @@ void HdmiCecController::setLanguage(hidl_string language) { // Enable audio return channel. void HdmiCecController::enableAudioReturnChannel(int port, bool enabled) { Return<void> ret = mHdmiCec->enableAudioReturnChannel(port, enabled); - if (!ret.getStatus().isOk()) { + if (!ret.isOk()) { ALOGE("Failed to enable/disable ARC."); } } @@ -310,7 +310,7 @@ void HdmiCecController::enableAudioReturnChannel(int port, bool enabled) { // Whether to hdmi device is connected to the given port. bool HdmiCecController::isConnected(int port) { Return<bool> ret = mHdmiCec->isConnected(port); - if (!ret.getStatus().isOk()) { + if (!ret.isOk()) { ALOGE("Failed to get connection info."); } return ret; diff --git a/services/core/jni/com_android_server_lights_LightsService.cpp b/services/core/jni/com_android_server_lights_LightsService.cpp index a3ab8f67d2fa..cf7f1cbfd0be 100644 --- a/services/core/jni/com_android_server_lights_LightsService.cpp +++ b/services/core/jni/com_android_server_lights_LightsService.cpp @@ -117,8 +117,7 @@ static void setLight_native( ALOGD_IF_SLOW(50, "Excessive delay setting light"); Return<Status> ret = gLight->setLight(type, state); - // TODO(b/31348667): this is transport specific status - if (!ret.getStatus().isOk()) { + if (!ret.isOk()) { ALOGE("Failed to issue set light command."); return; } diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index b7032dbb0ab0..09886db17af6 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -888,75 +888,68 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, // TODO(b/31632518) gnssHal = IGnss::getService("gnss"); if (gnssHal != nullptr) { - auto result = gnssHal->getExtensionXtra([](const sp<IGnssXtra>& xtraIface) { - gnssXtraIface = xtraIface; - }); - - if (!result.getStatus().isOk()) { + auto gnssXtra = gnssHal->getExtensionXtra(); + if (!gnssXtra.isOk()) { ALOGD("Unable to get a handle to Xtra"); + } else { + gnssXtraIface = gnssXtra; } - result = gnssHal->getExtensionAGnssRil([](const sp<IAGnssRil>& rilIface) { - agnssRilIface = rilIface; - }); - - if (!result.getStatus().isOk()) { + auto gnssRil = gnssHal->getExtensionAGnssRil(); + if (!gnssRil.isOk()) { ALOGD("Unable to get a handle to AGnssRil"); + } else { + agnssRilIface = gnssRil; } - result = gnssHal->getExtensionAGnss([](const sp<IAGnss>& assistedGnssIface) { - agnssIface = assistedGnssIface; - }); - - if (!result.getStatus().isOk()) { + auto gnssAgnss = gnssHal->getExtensionAGnss(); + if (!gnssAgnss.isOk()) { ALOGD("Unable to get a handle to AGnss"); + } else { + agnssIface = gnssAgnss; } - result = gnssHal->getExtensionGnssNavigationMessage( - [](const sp<IGnssNavigationMessage>& navigationMessageIface) { - gnssNavigationMessageIface = navigationMessageIface; - }); - - if (!result.getStatus().isOk()) { + auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage(); + if (!gnssNavigationMessage.isOk()) { ALOGD("Unable to get a handle to GnssNavigationMessage"); + } else { + gnssNavigationMessageIface = gnssNavigationMessage; } - result = gnssHal->getExtensionGnssMeasurement([]( - const sp<IGnssMeasurement>& measurementIface) { - gnssMeasurementIface = measurementIface; - }); - if (!result.getStatus().isOk()) { + auto gnssMeasurement = gnssHal->getExtensionGnssMeasurement(); + if (!gnssMeasurement.isOk()) { ALOGD("Unable to get a handle to GnssMeasurement"); + } else { + gnssMeasurementIface = gnssMeasurement; } - result = gnssHal->getExtensionGnssDebug([](const sp<IGnssDebug>& debugIface) { - gnssDebugIface = debugIface; - }); - if (!result.getStatus().isOk()) { + auto gnssDebug = gnssHal->getExtensionGnssDebug(); + if (!gnssDebug.isOk()) { ALOGD("Unable to get a handle to GnssDebug"); + } else { + gnssDebugIface = gnssDebug; } - result = gnssHal->getExtensionGnssNi([](const sp<IGnssNi>& niIface) { - gnssNiIface = niIface; - }); - if (!result.getStatus().isOk()) { + auto gnssNi = gnssHal->getExtensionGnssNi(); + if (!gnssNi.isOk()) { ALOGD("Unable to get a handle to GnssNi"); + } else { + gnssNiIface = gnssNi; } - result = gnssHal->getExtensionGnssConfiguration([](const sp<IGnssConfiguration>& configIface) { - gnssConfigurationIface = configIface; - }); - if (!result.getStatus().isOk()) { + auto gnssConfiguration = gnssHal->getExtensionGnssConfiguration(); + if (!gnssConfiguration.isOk()) { ALOGD("Unable to get a handle to GnssConfiguration"); + } else { + gnssConfigurationIface = gnssConfiguration; } - result = gnssHal->getExtensionGnssGeofencing([](const sp<IGnssGeofencing>& geofenceIface) { - gnssGeofencingIface = geofenceIface; - }); - if (!result.getStatus().isOk()) { + auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing(); + if (!gnssGeofencing.isOk()) { ALOGD("Unable to get a handle to GnssGeofencing"); + } else { + gnssGeofencingIface = gnssGeofencing; } - } else { ALOGE("Unable to get GPS service\n"); } @@ -994,7 +987,7 @@ static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject } auto result = gnssHal->setCallback(gnssCbIface); - if ((!result) || (!result.getStatus().isOk())) { + if ((!result) || (!result.isOk())) { ALOGE("SetCallback for Gnss Interface fails\n"); return JNI_FALSE; } @@ -1004,7 +997,7 @@ static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject ALOGE("Unable to initialize GNSS Xtra interface\n"); } else { result = gnssXtraIface->setCallback(gnssXtraCbIface); - if ((!result) || (!result.getStatus().isOk())) { + if ((!result) || (!result.isOk())) { gnssXtraIface = nullptr; ALOGE("SetCallback for Gnss Xtra Interface fails\n"); } @@ -1049,7 +1042,7 @@ static jboolean android_location_GnssLocationProvider_set_position_mode(JNIEnv* min_interval, preferred_accuracy, preferred_time); - if (!result.getStatus().isOk()) { + if (!result.isOk()) { ALOGE("%s: GNSS setPositionMode failed\n", __func__); return JNI_FALSE; } else { @@ -1063,7 +1056,7 @@ static jboolean android_location_GnssLocationProvider_set_position_mode(JNIEnv* static jboolean android_location_GnssLocationProvider_start(JNIEnv* /* env */, jobject /* obj */) { if (gnssHal != nullptr) { auto result = gnssHal->start(); - if (!result.getStatus().isOk()) { + if (!result.isOk()) { return JNI_FALSE; } else { return result; @@ -1076,7 +1069,7 @@ static jboolean android_location_GnssLocationProvider_start(JNIEnv* /* env */, j static jboolean android_location_GnssLocationProvider_stop(JNIEnv* /* env */, jobject /* obj */) { if (gnssHal != nullptr) { auto result = gnssHal->stop(); - if (!result.getStatus().isOk()) { + if (!result.isOk()) { return JNI_FALSE; } else { return result; @@ -1090,7 +1083,7 @@ static void android_location_GnssLocationProvider_delete_aiding_data(JNIEnv* /* jint flags) { if (gnssHal != nullptr) { auto result = gnssHal->deleteAidingData(static_cast<IGnss::GnssAidingData>(flags)); - if (!result.getStatus().isOk()) { + if (!result.isOk()) { ALOGE("Error in deleting aiding data"); } } @@ -1191,7 +1184,7 @@ static void android_location_GnssLocationProvider_inject_time(JNIEnv* /* env */, jlong time, jlong timeReference, jint uncertainty) { if (gnssHal != nullptr) { auto result = gnssHal->injectTime(time, timeReference, uncertainty); - if (!result || !result.getStatus().isOk()) { + if (!result || !result.isOk()) { ALOGE("%s: Gnss injectTime() failed", __func__); } } @@ -1201,7 +1194,7 @@ static void android_location_GnssLocationProvider_inject_location(JNIEnv* /* env jobject /* obj */, jdouble latitude, jdouble longitude, jfloat accuracy) { if (gnssHal != nullptr) { auto result = gnssHal->injectLocation(latitude, longitude, accuracy); - if (!result || !result.getStatus().isOk()) { + if (!result || !result.isOk()) { ALOGE("%s: Gnss injectLocation() failed", __func__); } } @@ -1238,7 +1231,7 @@ static void android_location_GnssLocationProvider_agps_data_conn_open( const char *apnStr = env->GetStringUTFChars(apn, NULL); auto result = agnssIface->dataConnOpen(apnStr, static_cast<IAGnss::ApnIpType>(apnIpType)); - if ((!result) || (!result.getStatus().isOk())) { + if ((!result) || (!result.isOk())) { ALOGE("%s: Failed to set APN and its IP type", __func__); } env->ReleaseStringUTFChars(apn, apnStr); @@ -1252,7 +1245,7 @@ static void android_location_GnssLocationProvider_agps_data_conn_closed(JNIEnv* } auto result = agnssIface->dataConnClosed(); - if ((!result) || (!result.getStatus().isOk())) { + if ((!result) || (!result.isOk())) { ALOGE("%s: Failed to close AGnss data connection", __func__); } } @@ -1265,7 +1258,7 @@ static void android_location_GnssLocationProvider_agps_data_conn_failed(JNIEnv* } auto result = agnssIface->dataConnFailed(); - if ((!result) || (!result.getStatus().isOk())) { + if ((!result) || (!result.isOk())) { ALOGE("%s: Failed to notify unavailability of AGnss data connection", __func__); } } @@ -1281,7 +1274,7 @@ static void android_location_GnssLocationProvider_set_agps_server(JNIEnv* env, j auto result = agnssIface->setServer(static_cast<IAGnssCallback::AGnssType>(type), c_hostname, port); - if ((!result) || (!result.getStatus().isOk())) { + if ((!result) || (!result.isOk())) { ALOGE("%s: Failed to set AGnss host name and port", __func__); } @@ -1354,13 +1347,13 @@ static void android_location_GnssLocationProvider_update_network_state(JNIEnv* e auto result = agnssRilIface->updateNetworkState(connected, static_cast<IAGnssRil::NetworkType>(type), roaming); - if ((!result) || (!result.getStatus().isOk())) { + if ((!result) || (!result.isOk())) { ALOGE("updateNetworkState failed"); } const char *c_apn = env->GetStringUTFChars(apn, NULL); result = agnssRilIface->updateNetworkAvailability(available, c_apn); - if ((!result) || (!result.getStatus().isOk())) { + if ((!result) || (!result.isOk())) { ALOGE("updateNetworkAvailability failed"); } @@ -1384,7 +1377,7 @@ static jboolean android_location_GnssLocationProvider_add_geofence(JNIEnv* /* en geofenceId, latitude, longitude, radius, static_cast<IGnssGeofenceCallback::GeofenceTransition>(last_transition), monitor_transition, notification_responsiveness, unknown_timer); - return boolToJbool(result.getStatus().isOk()); + return boolToJbool(result.isOk()); } else { ALOGE("Geofence Interface not available"); } @@ -1395,7 +1388,7 @@ static jboolean android_location_GnssLocationProvider_remove_geofence(JNIEnv* /* jobject /* obj */, jint geofenceId) { if (gnssGeofencingIface != nullptr) { auto result = gnssGeofencingIface->removeGeofence(geofenceId); - return boolToJbool(result.getStatus().isOk()); + return boolToJbool(result.isOk()); } else { ALOGE("Geofence interface not available"); } @@ -1406,7 +1399,7 @@ static jboolean android_location_GnssLocationProvider_pause_geofence(JNIEnv* /* jobject /* obj */, jint geofenceId) { if (gnssGeofencingIface != nullptr) { auto result = gnssGeofencingIface->pauseGeofence(geofenceId); - return boolToJbool(result.getStatus().isOk()); + return boolToJbool(result.isOk()); } else { ALOGE("Geofence interface not available"); } @@ -1417,7 +1410,7 @@ static jboolean android_location_GnssLocationProvider_resume_geofence(JNIEnv* /* jobject /* obj */, jint geofenceId, jint monitor_transition) { if (gnssGeofencingIface != nullptr) { auto result = gnssGeofencingIface->resumeGeofence(geofenceId, monitor_transition); - return boolToJbool(result.getStatus().isOk()); + return boolToJbool(result.isOk()); } else { ALOGE("Geofence interface not available"); } @@ -1463,7 +1456,7 @@ static jboolean android_location_GnssLocationProvider_stop_measurement_collectio } auto result = gnssMeasurementIface->close(); - return boolToJbool(result.getStatus().isOk()); + return boolToJbool(result.isOk()); } static jboolean android_location_GnssLocationProvider_is_navigation_message_supported( @@ -1505,7 +1498,7 @@ static jboolean android_location_GnssLocationProvider_stop_navigation_message_co } auto result = gnssNavigationMessageIface->close(); - return boolToJbool(result.getStatus().isOk()); + return boolToJbool(result.isOk()); } static jboolean android_location_GnssLocationProvider_set_emergency_supl_pdn(JNIEnv*, @@ -1517,7 +1510,7 @@ static jboolean android_location_GnssLocationProvider_set_emergency_supl_pdn(JNI } auto result = gnssConfigurationIface->setEmergencySuplPdn(emergencySuplPdn); - if (result.getStatus().isOk()) { + if (result.isOk()) { return result; } else { return JNI_FALSE; @@ -1532,7 +1525,7 @@ static jboolean android_location_GnssLocationProvider_set_supl_version(JNIEnv*, return JNI_FALSE; } auto result = gnssConfigurationIface->setSuplVersion(version); - if (result.getStatus().isOk()) { + if (result.isOk()) { return result; } else { return JNI_FALSE; @@ -1548,7 +1541,7 @@ static jboolean android_location_GnssLocationProvider_set_supl_es(JNIEnv*, } auto result = gnssConfigurationIface->setSuplEs(suplEs); - if (result.getStatus().isOk()) { + if (result.isOk()) { return result; } else { return JNI_FALSE; @@ -1564,7 +1557,7 @@ static jboolean android_location_GnssLocationProvider_set_supl_mode(JNIEnv*, } auto result = gnssConfigurationIface->setSuplMode(mode); - if (result.getStatus().isOk()) { + if (result.isOk()) { return result; } else { return JNI_FALSE; @@ -1580,7 +1573,7 @@ static jboolean android_location_GnssLocationProvider_set_gps_lock(JNIEnv*, } auto result = gnssConfigurationIface->setGpsLock(gpsLock); - if (result.getStatus().isOk()) { + if (result.isOk()) { return result; } else { return JNI_FALSE; @@ -1597,7 +1590,7 @@ static jboolean android_location_GnssLocationProvider_set_lpp_profile(JNIEnv*, auto result = gnssConfigurationIface->setLppProfile(lppProfile); - if (result.getStatus().isOk()) { + if (result.isOk()) { return result; } else { return JNI_FALSE; @@ -1613,7 +1606,7 @@ static jboolean android_location_GnssLocationProvider_set_gnss_pos_protocol_sele } auto result = gnssConfigurationIface->setGlonassPositioningProtocol(gnssPosProtocol); - if (result.getStatus().isOk()) { + if (result.isOk()) { return result; } else { return JNI_FALSE; diff --git a/services/coverage/Android.mk b/services/coverage/Android.mk new file mode 100644 index 000000000000..da99994d6b4c --- /dev/null +++ b/services/coverage/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := services.coverage + +LOCAL_SRC_FILES += \ + $(call all-java-files-under,java) + +LOCAL_JAVA_LIBRARIES := jacocoagent + +include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/services/coverage/java/com/android/server/coverage/CoverageService.java b/services/coverage/java/com/android/server/coverage/CoverageService.java new file mode 100644 index 000000000000..d600aa8c4276 --- /dev/null +++ b/services/coverage/java/com/android/server/coverage/CoverageService.java @@ -0,0 +1,145 @@ +/* + * 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.server.coverage; + +import android.os.Binder; +import android.os.ParcelFileDescriptor; +import android.os.ShellCallback; +import android.os.ShellCommand; +import android.os.ResultReceiver; + +import org.jacoco.agent.rt.RT; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * A service that responds to `cmd coverage ...` and provides a mechanism for dumping code coverage + * information from the system server process. + * @hide + */ +public class CoverageService extends Binder { + + public static final String COVERAGE_SERVICE = "coverage"; + public static final boolean ENABLED; + + static { + // This service should only be enabled if org.jacoco.agent.rt.RT was added to the build + boolean shouldEnable = true; + try { + Class.forName("org.jacoco.agent.rt.RT"); + } catch (ClassNotFoundException e) { + shouldEnable = false; + } + ENABLED = shouldEnable; + } + + /** + * {@inheritDoc} + */ + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, + String[] args, ShellCallback callback, ResultReceiver resultReceiver) { + new CoverageCommand().exec(this, in, out, err, args, callback, resultReceiver); + } + + /** + * A {@link ShellCommand} implementation for performing coverage shell commands. + */ + private static class CoverageCommand extends ShellCommand { + + /** + * {@inheritDoc} + */ + @Override + public int onCommand(String cmd) { + if ("dump".equals(cmd)) { + return onDump(); + } else if ("reset".equals(cmd)) { + return onReset(); + } else { + return handleDefaultCommands(cmd); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onHelp() { + PrintWriter pw = getOutPrintWriter(); + pw.println("Coverage commands:"); + pw.println(" help"); + pw.println(" Print this help text."); + pw.println(" dump [FILE]"); + pw.println(" Dump code coverage to FILE."); + pw.println(" reset"); + pw.println(" Reset coverage information."); + } + + /** + * Perform the "dump" command to write the collected execution data to a file. + * + * @return The command result. + */ + private int onDump() { + // Figure out where to dump the coverage data + String dest = getNextArg(); + if (dest == null) { + dest = "/data/local/tmp/coverage.ec"; + } else { + File f = new File(dest); + if (f.isDirectory()) { + dest = new File(f, "coverage.ec").getAbsolutePath(); + } + } + + // Try to open the destination file + ParcelFileDescriptor fd = openOutputFileForSystem(dest); + if (fd == null) { + return -1; + } + + // Write the execution data to the file + try (BufferedOutputStream output = new BufferedOutputStream( + new ParcelFileDescriptor.AutoCloseOutputStream(fd))) { + output.write(RT.getAgent().getExecutionData(false)); + output.flush(); + getOutPrintWriter().println(String.format("Dumped coverage data to %s", dest)); + } catch (IOException e) { + getErrPrintWriter().println("Failed to dump coverage data: " + e.getMessage()); + return -1; + } + + return 0; + } + + /** + * Perform the "reset" command to clear the collected execution data. + * + * @return The command result. + */ + private int onReset() { + RT.getAgent().reset(); + getOutPrintWriter().println("Reset coverage data"); + return 0; + } + } +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 22f9f3a05563..e5c8d02c4fd1 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5980,9 +5980,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { throw new IllegalArgumentException("Invalid component " + admin + " for device owner"); } - final boolean hasIncompatibleAccounts = hasIncompatibleAccountsNoLock(userId, admin); + final boolean hasIncompatibleAccountsOrNonAdb = + hasIncompatibleAccountsOrNonAdbNoLock(userId, admin); synchronized (this) { - enforceCanSetDeviceOwnerLocked(admin, userId, hasIncompatibleAccounts); + enforceCanSetDeviceOwnerLocked(admin, userId, hasIncompatibleAccountsOrNonAdb); final ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(admin, userId); if (activeAdmin == null || getUserData(userId).mRemovingAdmins.contains(admin)) { @@ -6218,9 +6219,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { throw new IllegalArgumentException("Component " + who + " not installed for userId:" + userHandle); } - final boolean hasIncompatibleAccounts = hasIncompatibleAccountsNoLock(userHandle, who); + final boolean hasIncompatibleAccountsOrNonAdb = + hasIncompatibleAccountsOrNonAdbNoLock(userHandle, who); synchronized (this) { - enforceCanSetProfileOwnerLocked(who, userHandle, hasIncompatibleAccounts); + enforceCanSetProfileOwnerLocked(who, userHandle, hasIncompatibleAccountsOrNonAdb); if (getActiveAdminUncheckedLocked(who, userHandle) == null || getUserData(userHandle).mRemovingAdmins.contains(who)) { @@ -6553,10 +6555,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * The profile owner can only be set before the user setup phase has completed, * except for: * - SYSTEM_UID - * - adb unless hasIncompatibleAccounts is true. + * - adb unless hasIncompatibleAccountsOrNonAdb is true. */ private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle, - boolean hasIncompatibleAccounts) { + boolean hasIncompatibleAccountsOrNonAdb) { UserInfo info = getUserInfo(userHandle); if (info == null) { // User doesn't exist. @@ -6576,7 +6578,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } if (isAdb()) { if ((mIsWatch || hasUserSetupCompleted(userHandle)) - && hasIncompatibleAccounts) { + && hasIncompatibleAccountsOrNonAdb) { throw new IllegalStateException("Not allowed to set the profile owner because " + "there are already some accounts on the profile"); } @@ -6594,13 +6596,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * permission. */ private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner, int userId, - boolean hasIncompatibleAccounts) { + boolean hasIncompatibleAccountsOrNonAdb) { if (!isAdb()) { enforceCanManageProfileAndDeviceOwners(); } final int code = checkDeviceOwnerProvisioningPreConditionLocked( - owner, userId, isAdb(), hasIncompatibleAccounts); + owner, userId, isAdb(), hasIncompatibleAccountsOrNonAdb); switch (code) { case CODE_OK: return; @@ -8909,7 +8911,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * except for adb command if no accounts or additional users are present on the device. */ private int checkDeviceOwnerProvisioningPreConditionLocked(@Nullable ComponentName owner, - int deviceOwnerUserId, boolean isAdb, boolean hasIncompatibleAccounts) { + int deviceOwnerUserId, boolean isAdb, boolean hasIncompatibleAccountsOrNonAdb) { if (mOwners.hasDeviceOwner()) { return CODE_HAS_DEVICE_OWNER; } @@ -8929,7 +8931,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (mUserManager.getUserCount() > 1) { return CODE_NONSYSTEM_USER_EXISTS; } - if (hasIncompatibleAccounts) { + if (hasIncompatibleAccountsOrNonAdb) { return CODE_ACCOUNTS_NOT_EMPTY; } } else { @@ -8956,9 +8958,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private int checkDeviceOwnerProvisioningPreCondition(int deviceOwnerUserId) { synchronized (this) { - // hasIncompatibleAccounts doesn't matter since the caller is not adb. + // hasIncompatibleAccountsOrNonAdb doesn't matter since the caller is not adb. return checkDeviceOwnerProvisioningPreConditionLocked(/* owner unknown */ null, - deviceOwnerUserId, /* isAdb= */ false, /* hasIncompatibleAccounts=*/ true); + deviceOwnerUserId, /* isAdb= */ false, + /* hasIncompatibleAccountsOrNonAdb=*/ true); } } @@ -9857,9 +9860,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * ..._DISALLOWED, return true. * - Otherwise return false. * + * If the caller is *not* ADB, it also returns true. The returned value shouldn't be used + * when the caller is not ADB. + * * DO NOT CALL IT WITH THE DPMS LOCK HELD. */ - private boolean hasIncompatibleAccountsNoLock(int userId, @Nullable ComponentName owner) { + private boolean hasIncompatibleAccountsOrNonAdbNoLock( + int userId, @Nullable ComponentName owner) { + if (!isAdb()) { + return true; + } if (Thread.holdsLock(this)) { Slog.wtf(LOG_TAG, "hasIncompatibleAccountsNoLock() called with the DPMS lock held."); return true; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 7ccae2c323e4..1d550d294560 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -63,6 +63,7 @@ import com.android.server.camera.CameraService; import com.android.server.clipboard.ClipboardService; import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.connectivity.MetricsLoggerService; +import com.android.server.coverage.CoverageService; import com.android.server.devicepolicy.DevicePolicyManagerService; import com.android.server.display.DisplayManagerService; import com.android.server.display.NightDisplayService; @@ -1260,6 +1261,12 @@ public final class SystemServer { traceEnd(); } + if (!disableNonCoreServices && CoverageService.ENABLED) { + traceBeginAndSlog("AddCoverageService"); + ServiceManager.addService(CoverageService.COVERAGE_SERVICE, new CoverageService()); + traceEnd(); + } + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) { traceBeginAndSlog("StartPrintManager"); mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 469dea5a7259..d39245992d49 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -1208,8 +1208,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { ); verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(UserHandle.USER_SYSTEM), - MockUtils.checkUserRestrictions(), - MockUtils.checkUserRestrictions(defaultRestrictions) + MockUtils.checkUserRestrictions(defaultRestrictions), + MockUtils.checkUserRestrictions() ); reset(mContext.userManagerInternal); @@ -1479,8 +1479,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { ); verify(mContext.userManagerInternal).setDevicePolicyUserRestrictions( eq(UserHandle.USER_SYSTEM), - MockUtils.checkUserRestrictions(), - MockUtils.checkUserRestrictions(defaultRestrictions) + MockUtils.checkUserRestrictions(defaultRestrictions), + MockUtils.checkUserRestrictions() ); reset(mContext.userManagerInternal); @@ -1521,8 +1521,8 @@ public class DevicePolicyManagerTest extends DpmTestBase { ); verify(mContext.userManagerInternal, atLeast(1)).setDevicePolicyUserRestrictions( eq(UserHandle.USER_SYSTEM), - MockUtils.checkUserRestrictions(), - MockUtils.checkUserRestrictions(newDefaultEnabledRestriction) + MockUtils.checkUserRestrictions(newDefaultEnabledRestriction), + MockUtils.checkUserRestrictions() ); reset(mContext.userManagerInternal); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index 84bb4e889793..e30bd5d96672 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -15,14 +15,37 @@ */ package com.android.server.pm; +import android.annotation.TestApi; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ConfigurationInfo; +import android.content.pm.FeatureGroupInfo; +import android.content.pm.FeatureInfo; +import android.content.pm.InstrumentationInfo; import android.content.pm.PackageParser; +import android.content.pm.ProviderInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.Signature; +import android.os.Bundle; +import android.os.Parcel; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.MediumTest; import java.io.File; +import java.lang.reflect.Array; +import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import static org.junit.Assert.*; + +import android.util.ArrayMap; +import android.util.ArraySet; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -87,6 +110,36 @@ public class PackageParserTest { assertEquals("android", pkg.packageName); } + @Test + public void test_serializePackage() throws Exception { + PackageParser pp = new PackageParser(); + pp.setCacheDir(mTmpDir); + + PackageParser.Package pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, + true /* useCaches */); + + Parcel p = Parcel.obtain(); + pkg.writeToParcel(p, 0 /* flags */); + + p.setDataPosition(0); + PackageParser.Package deserialized = new PackageParser.Package(p); + + assertPackagesEqual(pkg, deserialized); + } + + @Test + public void test_roundTripKnownFields() throws Exception { + PackageParser.Package pkg = new PackageParser.Package("foo"); + setKnownFields(pkg); + + Parcel p = Parcel.obtain(); + pkg.writeToParcel(p, 0 /* flags */); + + p.setDataPosition(0); + PackageParser.Package deserialized = new PackageParser.Package(p); + assertAllFieldsExist(deserialized); + } + /** * A trivial subclass of package parser that only caches the package name, and throws away * all other information. @@ -102,4 +155,375 @@ public class PackageParserTest { return new Package(new String(cacheEntry, StandardCharsets.UTF_8)); } } + + // NOTE: The equality assertions below are based on code autogenerated by IntelliJ. + + public static void assertPackagesEqual(PackageParser.Package a, PackageParser.Package b) { + assertEquals(a.baseRevisionCode, b.baseRevisionCode); + assertEquals(a.baseHardwareAccelerated, b.baseHardwareAccelerated); + assertEquals(a.mVersionCode, b.mVersionCode); + assertEquals(a.mSharedUserLabel, b.mSharedUserLabel); + assertEquals(a.mPreferredOrder, b.mPreferredOrder); + assertEquals(a.installLocation, b.installLocation); + assertEquals(a.coreApp, b.coreApp); + assertEquals(a.mRequiredForAllUsers, b.mRequiredForAllUsers); + assertEquals(a.mOverlayPriority, b.mOverlayPriority); + assertEquals(a.mTrustedOverlay, b.mTrustedOverlay); + assertEquals(a.use32bitAbi, b.use32bitAbi); + assertEquals(a.packageName, b.packageName); + assertTrue(Arrays.equals(a.splitNames, b.splitNames)); + assertEquals(a.volumeUuid, b.volumeUuid); + assertEquals(a.codePath, b.codePath); + assertEquals(a.baseCodePath, b.baseCodePath); + assertTrue(Arrays.equals(a.splitCodePaths, b.splitCodePaths)); + assertTrue(Arrays.equals(a.splitRevisionCodes, b.splitRevisionCodes)); + assertTrue(Arrays.equals(a.splitFlags, b.splitFlags)); + assertTrue(Arrays.equals(a.splitPrivateFlags, b.splitPrivateFlags)); + assertApplicationInfoEqual(a.applicationInfo, b.applicationInfo); + + assertEquals(a.permissions.size(), b.permissions.size()); + for (int i = 0; i < a.permissions.size(); ++i) { + assertPermissionsEqual(a.permissions.get(i), b.permissions.get(i)); + assertSame(a.permissions.get(i).owner, a); + assertSame(b.permissions.get(i).owner, b); + } + + assertEquals(a.permissionGroups.size(), b.permissionGroups.size()); + for (int i = 0; i < a.permissionGroups.size(); ++i) { + assertPermissionGroupsEqual(a.permissionGroups.get(i), b.permissionGroups.get(i)); + } + + assertEquals(a.activities.size(), b.activities.size()); + for (int i = 0; i < a.activities.size(); ++i) { + assertActivitiesEqual(a.activities.get(i), b.activities.get(i)); + } + + assertEquals(a.receivers.size(), b.receivers.size()); + for (int i = 0; i < a.receivers.size(); ++i) { + assertActivitiesEqual(a.receivers.get(i), b.receivers.get(i)); + } + + assertEquals(a.providers.size(), b.providers.size()); + for (int i = 0; i < a.providers.size(); ++i) { + assertProvidersEqual(a.providers.get(i), b.providers.get(i)); + } + + assertEquals(a.services.size(), b.services.size()); + for (int i = 0; i < a.services.size(); ++i) { + assertServicesEqual(a.services.get(i), b.services.get(i)); + } + + assertEquals(a.instrumentation.size(), b.instrumentation.size()); + for (int i = 0; i < a.instrumentation.size(); ++i) { + assertInstrumentationEqual(a.instrumentation.get(i), b.instrumentation.get(i)); + } + + assertEquals(a.requestedPermissions, b.requestedPermissions); + assertEquals(a.protectedBroadcasts, b.protectedBroadcasts); + assertEquals(a.parentPackage, b.parentPackage); + assertEquals(a.childPackages, b.childPackages); + assertEquals(a.libraryNames, b.libraryNames); + assertEquals(a.usesLibraries, b.usesLibraries); + assertEquals(a.usesOptionalLibraries, b.usesOptionalLibraries); + assertTrue(Arrays.equals(a.usesLibraryFiles, b.usesLibraryFiles)); + assertEquals(a.mOriginalPackages, b.mOriginalPackages); + assertEquals(a.mRealPackage, b.mRealPackage); + assertEquals(a.mAdoptPermissions, b.mAdoptPermissions); + assertBundleApproximateEquals(a.mAppMetaData, b.mAppMetaData); + assertEquals(a.mVersionName, b.mVersionName); + assertEquals(a.mSharedUserId, b.mSharedUserId); + assertTrue(Arrays.equals(a.mSignatures, b.mSignatures)); + assertTrue(Arrays.equals(a.mCertificates, b.mCertificates)); + assertTrue(Arrays.equals(a.mLastPackageUsageTimeInMills, b.mLastPackageUsageTimeInMills)); + assertEquals(a.mExtras, b.mExtras); + assertEquals(a.mRestrictedAccountType, b.mRestrictedAccountType); + assertEquals(a.mRequiredAccountType, b.mRequiredAccountType); + assertEquals(a.mOverlayTarget, b.mOverlayTarget); + assertEquals(a.mSigningKeys, b.mSigningKeys); + assertEquals(a.mUpgradeKeySets, b.mUpgradeKeySets); + assertEquals(a.mKeySetMapping, b.mKeySetMapping); + assertEquals(a.cpuAbiOverride, b.cpuAbiOverride); + assertTrue(Arrays.equals(a.restrictUpdateHash, b.restrictUpdateHash)); + } + + private static void assertBundleApproximateEquals(Bundle a, Bundle b) { + if (a == b) { + return; + } + + // Force the bundles to be unparceled. + a.getBoolean("foo"); + b.getBoolean("foo"); + + assertEquals(a.toString(), b.toString()); + } + + private static void assertComponentsEqual(PackageParser.Component<?> a, + PackageParser.Component<?> b) { + assertEquals(a.className, b.className); + assertBundleApproximateEquals(a.metaData, b.metaData); + assertEquals(a.getComponentName(), b.getComponentName()); + + if (a.intents != null && b.intents != null) { + assertEquals(a.intents.size(), b.intents.size()); + } else if (a.intents == null || b.intents == null) { + return; + } + + for (int i = 0; i < a.intents.size(); ++i) { + PackageParser.IntentInfo aIntent = a.intents.get(i); + PackageParser.IntentInfo bIntent = b.intents.get(i); + + assertEquals(aIntent.hasDefault, bIntent.hasDefault); + assertEquals(aIntent.labelRes, bIntent.labelRes); + assertEquals(aIntent.nonLocalizedLabel, bIntent.nonLocalizedLabel); + assertEquals(aIntent.icon, bIntent.icon); + assertEquals(aIntent.logo, bIntent.logo); + assertEquals(aIntent.banner, bIntent.banner); + assertEquals(aIntent.preferred, bIntent.preferred); + } + } + + private static void assertPermissionsEqual(PackageParser.Permission a, + PackageParser.Permission b) { + assertComponentsEqual(a, b); + assertEquals(a.tree, b.tree); + + // Verify basic flags in PermissionInfo to make sure they're consistent. We don't perform + // a full structural equality here because the code that serializes them isn't parser + // specific and is tested elsewhere. + assertEquals(a.info.protectionLevel, b.info.protectionLevel); + assertEquals(a.info.group, b.info.group); + assertEquals(a.info.flags, b.info.flags); + + if (a.group != null && b.group != null) { + assertPermissionGroupsEqual(a.group, b.group); + } else if (a.group != null || b.group != null) { + throw new AssertionError(); + } + } + + private static void assertInstrumentationEqual(PackageParser.Instrumentation a, + PackageParser.Instrumentation b) { + assertComponentsEqual(a, b); + + // Sanity check for InstrumentationInfo. + assertEquals(a.info.targetPackage, b.info.targetPackage); + assertEquals(a.info.sourceDir, b.info.sourceDir); + assertEquals(a.info.publicSourceDir, b.info.publicSourceDir); + } + + private static void assertServicesEqual(PackageParser.Service a, PackageParser.Service b) { + assertComponentsEqual(a, b); + + // Sanity check for ServiceInfo. + assertApplicationInfoEqual(a.info.applicationInfo, b.info.applicationInfo); + assertEquals(a.info.name, b.info.name); + } + + private static void assertProvidersEqual(PackageParser.Provider a, PackageParser.Provider b) { + assertComponentsEqual(a, b); + + // Sanity check for ProviderInfo + assertApplicationInfoEqual(a.info.applicationInfo, b.info.applicationInfo); + assertEquals(a.info.name, b.info.name); + } + + private static void assertActivitiesEqual(PackageParser.Activity a, PackageParser.Activity b) { + assertComponentsEqual(a, b); + + // Sanity check for ActivityInfo. + assertApplicationInfoEqual(a.info.applicationInfo, b.info.applicationInfo); + assertEquals(a.info.name, b.info.name); + } + + private static void assertPermissionGroupsEqual(PackageParser.PermissionGroup a, + PackageParser.PermissionGroup b) { + assertComponentsEqual(a, b); + + // Sanity check for PermissionGroupInfo. + assertEquals(a.info.name, b.info.name); + assertEquals(a.info.descriptionRes, b.info.descriptionRes); + } + + private static void assertApplicationInfoEqual(ApplicationInfo a, ApplicationInfo that) { + assertEquals(a.descriptionRes, that.descriptionRes); + assertEquals(a.theme, that.theme); + assertEquals(a.fullBackupContent, that.fullBackupContent); + assertEquals(a.uiOptions, that.uiOptions); + assertEquals(a.flags, that.flags); + assertEquals(a.privateFlags, that.privateFlags); + assertEquals(a.requiresSmallestWidthDp, that.requiresSmallestWidthDp); + assertEquals(a.compatibleWidthLimitDp, that.compatibleWidthLimitDp); + assertEquals(a.largestWidthLimitDp, that.largestWidthLimitDp); + assertEquals(a.nativeLibraryRootRequiresIsa, that.nativeLibraryRootRequiresIsa); + assertEquals(a.uid, that.uid); + assertEquals(a.minSdkVersion, that.minSdkVersion); + assertEquals(a.targetSdkVersion, that.targetSdkVersion); + assertEquals(a.versionCode, that.versionCode); + assertEquals(a.enabled, that.enabled); + assertEquals(a.enabledSetting, that.enabledSetting); + assertEquals(a.installLocation, that.installLocation); + assertEquals(a.networkSecurityConfigRes, that.networkSecurityConfigRes); + assertEquals(a.taskAffinity, that.taskAffinity); + assertEquals(a.permission, that.permission); + assertEquals(a.processName, that.processName); + assertEquals(a.className, that.className); + assertEquals(a.manageSpaceActivityName, that.manageSpaceActivityName); + assertEquals(a.backupAgentName, that.backupAgentName); + assertEquals(a.volumeUuid, that.volumeUuid); + assertEquals(a.scanSourceDir, that.scanSourceDir); + assertEquals(a.scanPublicSourceDir, that.scanPublicSourceDir); + assertEquals(a.sourceDir, that.sourceDir); + assertEquals(a.publicSourceDir, that.publicSourceDir); + assertTrue(Arrays.equals(a.splitSourceDirs, that.splitSourceDirs)); + assertTrue(Arrays.equals(a.splitPublicSourceDirs, that.splitPublicSourceDirs)); + assertTrue(Arrays.equals(a.resourceDirs, that.resourceDirs)); + assertEquals(a.seinfo, that.seinfo); + assertTrue(Arrays.equals(a.sharedLibraryFiles, that.sharedLibraryFiles)); + assertEquals(a.dataDir, that.dataDir); + assertEquals(a.deviceProtectedDataDir, that.deviceProtectedDataDir); + assertEquals(a.deviceEncryptedDataDir, that.deviceEncryptedDataDir); + assertEquals(a.credentialProtectedDataDir, that.credentialProtectedDataDir); + assertEquals(a.credentialEncryptedDataDir, that.credentialEncryptedDataDir); + assertEquals(a.nativeLibraryDir, that.nativeLibraryDir); + assertEquals(a.secondaryNativeLibraryDir, that.secondaryNativeLibraryDir); + assertEquals(a.nativeLibraryRootDir, that.nativeLibraryRootDir); + assertEquals(a.primaryCpuAbi, that.primaryCpuAbi); + assertEquals(a.secondaryCpuAbi, that.secondaryCpuAbi); + } + + public static void setKnownFields(PackageParser.Package pkg) { + pkg.baseRevisionCode = 100; + pkg.baseHardwareAccelerated = true; + pkg.mVersionCode = 100; + pkg.mSharedUserLabel = 100; + pkg.mPreferredOrder = 100; + pkg.installLocation = 100; + pkg.coreApp = true; + pkg.mRequiredForAllUsers = true; + pkg.mOverlayPriority = 100; + pkg.mTrustedOverlay = true; + pkg.use32bitAbi = true; + pkg.packageName = "foo"; + pkg.splitNames = new String[] { "foo" }; + pkg.volumeUuid = "foo"; + pkg.codePath = "foo"; + pkg.baseCodePath = "foo"; + pkg.splitCodePaths = new String[] { "foo" }; + pkg.splitRevisionCodes = new int[] { 100 }; + pkg.splitFlags = new int[] { 100 }; + pkg.splitPrivateFlags = new int[] { 100 }; + pkg.applicationInfo = new ApplicationInfo(); + + pkg.permissions.add(new PackageParser.Permission(pkg)); + pkg.permissionGroups.add(new PackageParser.PermissionGroup(pkg)); + + final PackageParser.ParseComponentArgs dummy = new PackageParser.ParseComponentArgs( + pkg, new String[1], 0, 0, 0, 0, 0, 0, null, 0, 0, 0); + + pkg.activities.add(new PackageParser.Activity(dummy, new ActivityInfo())); + pkg.receivers.add(new PackageParser.Activity(dummy, new ActivityInfo())); + pkg.providers.add(new PackageParser.Provider(dummy, new ProviderInfo())); + pkg.services.add(new PackageParser.Service(dummy, new ServiceInfo())); + pkg.instrumentation.add(new PackageParser.Instrumentation(dummy, new InstrumentationInfo())); + pkg.requestedPermissions.add("foo"); + + pkg.protectedBroadcasts = new ArrayList<>(); + pkg.protectedBroadcasts.add("foo"); + + pkg.parentPackage = new PackageParser.Package("foo"); + + pkg.childPackages = new ArrayList<>(); + pkg.childPackages.add(new PackageParser.Package("bar")); + + pkg.libraryNames = new ArrayList<>(); + pkg.libraryNames.add("foo"); + + pkg.usesLibraries = new ArrayList<>(); + pkg.usesLibraries.add("foo"); + + pkg.usesOptionalLibraries = new ArrayList<>(); + pkg.usesOptionalLibraries.add("foo"); + + pkg.usesLibraryFiles = new String[] { "foo "}; + + pkg.mOriginalPackages = new ArrayList<>(); + pkg.mOriginalPackages.add("foo"); + + pkg.mRealPackage = "foo"; + + pkg.mAdoptPermissions = new ArrayList<>(); + pkg.mAdoptPermissions.add("foo"); + + pkg.mAppMetaData = new Bundle(); + pkg.mVersionName = "foo"; + pkg.mSharedUserId = "foo"; + pkg.mSignatures = new Signature[] { new Signature(new byte[16]) }; + pkg.mCertificates = new Certificate[][] { new Certificate[] { null }}; + pkg.mExtras = new Bundle(); + pkg.mRestrictedAccountType = "foo"; + pkg.mRequiredAccountType = "foo"; + pkg.mOverlayTarget = "foo"; + pkg.mSigningKeys = new ArraySet<>(); + pkg.mUpgradeKeySets = new ArraySet<>(); + pkg.mKeySetMapping = new ArrayMap<>(); + pkg.cpuAbiOverride = "foo"; + pkg.restrictUpdateHash = new byte[16]; + + pkg.preferredActivityFilters = new ArrayList<>(); + pkg.preferredActivityFilters.add(new PackageParser.ActivityIntentInfo( + new PackageParser.Activity(dummy, new ActivityInfo()))); + + pkg.configPreferences = new ArrayList<>(); + pkg.configPreferences.add(new ConfigurationInfo()); + + pkg.reqFeatures = new ArrayList<>(); + pkg.reqFeatures.add(new FeatureInfo()); + + pkg.featureGroups = new ArrayList<>(); + pkg.featureGroups.add(new FeatureGroupInfo()); + } + + private static void assertAllFieldsExist(PackageParser.Package pkg) throws Exception { + Field[] fields = PackageParser.Package.class.getDeclaredFields(); + + Set<String> nonSerializedFields = new HashSet<>(); + nonSerializedFields.add("mExtras"); + nonSerializedFields.add("packageUsageTimeMillis"); + + for (Field f : fields) { + final Class<?> fieldType = f.getType(); + + if (nonSerializedFields.contains(f.getName())) { + continue; + } + + if (List.class.isAssignableFrom(fieldType)) { + // Sanity check for list fields: Assume they're non-null and contain precisely + // one element. + List<?> list = (List<?>) f.get(pkg); + assertNotNull(list); + assertEquals(1, list.size()); + } else if (fieldType.getComponentType() != null) { + // Sanity check for array fields: Assume they're non-null and contain precisely + // one element. + Object array = f.get(pkg); + assertNotNull(Array.get(array, 0)); + } else if (fieldType == String.class) { + // String fields: Check that they're set to "foo". + String value = (String) f.get(pkg); + assertEquals("foo", value); + } else if (fieldType == int.class) { + // int fields: Check that they're set to 100. + int value = (int) f.get(pkg); + assertEquals(100, value); + } else { + // All other fields: Check that they're set. + Object o = f.get(pkg); + assertNotNull("Field was null: " + f.getName(), o); + } + } + } } + diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java index 746310217c38..24893a142b20 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java @@ -47,14 +47,15 @@ public class TaskStackContainersTests extends WindowTestsBase { // Test that always-on-top stack can't be moved to position other than top. final TaskStack stack1 = createTaskStackOnDisplay(sDisplayContent); final TaskStack stack2 = createTaskStackOnDisplay(sDisplayContent); - sDisplayContent.addStackToDisplay(PINNED_STACK_ID, true); - final TaskStack pinnedStack = sWm.mStackIdToStack.get(PINNED_STACK_ID); + final TaskStack pinnedStack = addPinnedStack(); final WindowContainer taskStackContainer = stack1.getParent(); final int stack1Pos = taskStackContainer.mChildren.indexOf(stack1); final int stack2Pos = taskStackContainer.mChildren.indexOf(stack2); final int pinnedStackPos = taskStackContainer.mChildren.indexOf(pinnedStack); + assertGreaterThan(pinnedStackPos, stack2Pos); + assertGreaterThan(stack2Pos, stack1Pos); taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, pinnedStack, false); assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1); @@ -66,4 +67,43 @@ public class TaskStackContainersTests extends WindowTestsBase { assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2); assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), pinnedStack); } + @Test + public void testStackPositionBelowPinnedStack() throws Exception { + // Test that no stack can be above pinned stack. + final TaskStack pinnedStack = addPinnedStack(); + final TaskStack stack1 = createTaskStackOnDisplay(sDisplayContent); + + final WindowContainer taskStackContainer = stack1.getParent(); + + final int stackPos = taskStackContainer.mChildren.indexOf(stack1); + final int pinnedStackPos = taskStackContainer.mChildren.indexOf(pinnedStack); + assertGreaterThan(pinnedStackPos, stackPos); + + taskStackContainer.positionChildAt(WindowContainer.POSITION_TOP, stack1, false); + assertEquals(taskStackContainer.mChildren.get(stackPos), stack1); + assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), pinnedStack); + + taskStackContainer.positionChildAt(taskStackContainer.mChildren.size() - 1, stack1, false); + assertEquals(taskStackContainer.mChildren.get(stackPos), stack1); + assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), pinnedStack); + } + + private TaskStack addPinnedStack() { + TaskStack pinnedStack = sWm.mStackIdToStack.get(PINNED_STACK_ID); + if (pinnedStack == null) { + sDisplayContent.addStackToDisplay(PINNED_STACK_ID, true); + pinnedStack = sWm.mStackIdToStack.get(PINNED_STACK_ID); + } + + if (!pinnedStack.isVisible()) { + // Stack should contain visible app window to be considered visible. + final Task pinnedTask = createTaskInStack(pinnedStack, 0 /* userId */); + assertFalse(pinnedStack.isVisible()); + final TestAppWindowToken pinnedApp = new TestAppWindowToken(sDisplayContent); + pinnedTask.addChild(pinnedApp, 0 /* addPos */); + assertTrue(pinnedStack.isVisible()); + } + + return pinnedStack; + } } |