diff options
52 files changed, 1695 insertions, 698 deletions
diff --git a/api/current.txt b/api/current.txt index 8d2e9c8b9a54..dc926045642f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -37082,6 +37082,13 @@ package android.service.autofill { field public static final android.os.Parcelable.Creator<android.service.autofill.LuhnChecksumValidator> CREATOR; } + public final class RegexValidator implements android.os.Parcelable android.service.autofill.Validator { + ctor public RegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR; + } + public final class SaveCallback { method public void onFailure(java.lang.CharSequence); method public void onSuccess(); @@ -37122,13 +37129,6 @@ package android.service.autofill { field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR; } - public final class SimpleRegexValidator implements android.os.Parcelable android.service.autofill.Validator { - ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern); - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.service.autofill.SimpleRegexValidator> CREATOR; - } - public abstract interface Transformation { } diff --git a/api/system-current.txt b/api/system-current.txt index d2043fa850d9..da9f9eb911fe 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -40268,6 +40268,13 @@ package android.service.autofill { field public static final android.os.Parcelable.Creator<android.service.autofill.LuhnChecksumValidator> CREATOR; } + public final class RegexValidator implements android.os.Parcelable android.service.autofill.Validator { + ctor public RegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR; + } + public final class SaveCallback { method public void onFailure(java.lang.CharSequence); method public void onSuccess(); @@ -40308,13 +40315,6 @@ package android.service.autofill { field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR; } - public final class SimpleRegexValidator implements android.os.Parcelable android.service.autofill.Validator { - ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern); - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.service.autofill.SimpleRegexValidator> CREATOR; - } - public abstract interface Transformation { } diff --git a/api/test-current.txt b/api/test-current.txt index fcb404edff99..3e274701ebca 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -37274,6 +37274,14 @@ package android.service.autofill { field public static final android.os.Parcelable.Creator<android.service.autofill.LuhnChecksumValidator> CREATOR; } + public final class RegexValidator implements android.os.Parcelable android.service.autofill.Validator { + ctor public RegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern); + method public int describeContents(); + method public boolean isValid(android.service.autofill.ValueFinder); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR; + } + public final class SaveCallback { method public void onFailure(java.lang.CharSequence); method public void onSuccess(); @@ -37314,14 +37322,6 @@ package android.service.autofill { field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR; } - public final class SimpleRegexValidator implements android.os.Parcelable android.service.autofill.Validator { - ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern); - method public int describeContents(); - method public boolean isValid(android.service.autofill.ValueFinder); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.service.autofill.SimpleRegexValidator> CREATOR; - } - public abstract interface Transformation { } diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java index 371cd127bd5e..f99d1a8e06a4 100644 --- a/core/java/android/app/DexLoadReporter.java +++ b/core/java/android/app/DexLoadReporter.java @@ -19,6 +19,7 @@ package android.app; import android.os.FileUtils; import android.os.RemoteException; import android.os.SystemProperties; +import android.system.ErrnoException; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -26,6 +27,8 @@ import com.android.internal.annotations.GuardedBy; import dalvik.system.BaseDexClassLoader; import dalvik.system.VMRuntime; +import libcore.io.Libcore; + import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -151,22 +154,50 @@ import java.util.Set; // The dex path is not a secondary dex file. Nothing to do. return; } - File secondaryProfile = getSecondaryProfileFile(dexPath); + + File realDexPath; + try { + // Secondary dex profiles are stored in the oat directory, next to the real dex file + // and have the same name with 'cur.prof' appended. We use the realpath because that + // is what installd is using when processing the dex file. + // NOTE: Keep in sync with installd. + realDexPath = new File(Libcore.os.realpath(dexPath)); + } catch (ErrnoException ex) { + Slog.e(TAG, "Failed to get the real path of secondary dex " + dexPath + + ":" + ex.getMessage()); + // Do not continue with registration if we could not retrieve the real path. + return; + } + + // NOTE: Keep this in sync with installd expectations. + File secondaryProfileDir = new File(realDexPath.getParent(), "oat"); + File secondaryProfile = new File(secondaryProfileDir, realDexPath.getName() + ".cur.prof"); + + // Create the profile if not already there. + // Returns true if the file was created, false if the file already exists. + // or throws exceptions in case of errors. + if (!secondaryProfileDir.exists()) { + if (!secondaryProfileDir.mkdir()) { + Slog.e(TAG, "Could not create the profile directory: " + secondaryProfile); + // Do not continue with registration if we could not create the oat dir. + return; + } + } + try { - // Create the profile if not already there. - // Returns true if the file was created, false if the file already exists. - // or throws exceptions in case of errors. boolean created = secondaryProfile.createNewFile(); if (DEBUG && created) { Slog.i(TAG, "Created profile for secondary dex: " + secondaryProfile); } } catch (IOException ex) { - Slog.e(TAG, "Failed to create profile for secondary dex " + secondaryProfile + - ":" + ex.getMessage()); - // Don't move forward with the registration if we failed to create the profile. + Slog.e(TAG, "Failed to create profile for secondary dex " + dexPath + + ":" + ex.getMessage()); + // Do not continue with registration if we could not create the profile files. return; } + // If we got here, the dex paths is a secondary dex and we were able to create the profile. + // Register the path to the runtime. VMRuntime.registerAppInfo(secondaryProfile.getPath(), new String[] { dexPath }); } @@ -180,11 +211,4 @@ import java.util.Set; } return false; } - - // Secondary dex profiles are stored next to the dex file and have the same - // name with '.prof' appended. - // NOTE: Keep in sync with installd. - private File getSecondaryProfileFile(String dexPath) { - return new File(dexPath + ".prof"); - } } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index e8eca8ca7aa2..4ae1aaf71349 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -50,6 +50,8 @@ import android.app.ActivityManager; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageParserCacheHelper.ReadHelper; +import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; import android.content.pm.split.SplitAssetLoader; @@ -121,6 +123,7 @@ import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.zip.ZipEntry; @@ -235,6 +238,11 @@ public class PackageParser { private static final boolean LOG_UNSAFE_BROADCASTS = false; + /** + * Total number of packages that were read from the cache. We use it only for logging. + */ + public static final AtomicInteger sCachedPackageReadCount = new AtomicInteger(); + // Set of broadcast actions that are safe for manifest receivers private static final Set<String> SAFE_BROADCASTS = new ArraySet<>(); static { @@ -1035,21 +1043,45 @@ public class PackageParser { } @VisibleForTesting - protected Package fromCacheEntry(byte[] bytes) throws IOException { - Parcel p = Parcel.obtain(); + protected Package fromCacheEntry(byte[] bytes) { + return fromCacheEntryStatic(bytes); + } + + /** static version of {@link #fromCacheEntry} for unit tests. */ + @VisibleForTesting + public static Package fromCacheEntryStatic(byte[] bytes) { + final Parcel p = Parcel.obtain(); p.unmarshall(bytes, 0, bytes.length); p.setDataPosition(0); + final ReadHelper helper = new ReadHelper(p); + helper.startAndInstall(); + PackageParser.Package pkg = new PackageParser.Package(p); + p.recycle(); + sCachedPackageReadCount.incrementAndGet(); + return pkg; } @VisibleForTesting - protected byte[] toCacheEntry(Package pkg) throws IOException { - Parcel p = Parcel.obtain(); + protected byte[] toCacheEntry(Package pkg) { + return toCacheEntryStatic(pkg); + + } + + /** static version of {@link #toCacheEntry} for unit tests. */ + @VisibleForTesting + public static byte[] toCacheEntryStatic(Package pkg) { + final Parcel p = Parcel.obtain(); + final WriteHelper helper = new WriteHelper(p); + pkg.writeToParcel(p, 0 /* flags */); + + helper.finishAndUninstall(); + byte[] serialized = p.marshall(); p.recycle(); @@ -1146,13 +1178,7 @@ public class PackageParser { } } - final byte[] cacheEntry; - try { - cacheEntry = toCacheEntry(parsed); - } catch (IOException ioe) { - Slog.e(TAG, "Unable to serialize parsed package for: " + packageFile); - return; - } + final byte[] cacheEntry = toCacheEntry(parsed); if (cacheEntry == null) { return; @@ -6421,8 +6447,11 @@ public class PackageParser { dest.writeStringList(requestedPermissions); dest.writeStringList(protectedBroadcasts); + + // TODO: This doesn't work: b/64295061 dest.writeParcelable(parentPackage, flags); dest.writeParcelableList(childPackages, flags); + dest.writeString(staticSharedLibName); dest.writeInt(staticSharedLibVersion); dest.writeStringList(libraryNames); diff --git a/core/java/android/content/pm/PackageParserCacheHelper.java b/core/java/android/content/pm/PackageParserCacheHelper.java new file mode 100644 index 000000000000..44def3321c34 --- /dev/null +++ b/core/java/android/content/pm/PackageParserCacheHelper.java @@ -0,0 +1,157 @@ +/* + * 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. + */ + +package android.content.pm; + +import android.os.Parcel; +import android.util.Log; + +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Helper classes to read from and write to Parcel with pooled strings. + * + * @hide + */ +public class PackageParserCacheHelper { + private PackageParserCacheHelper() { + } + + private static final String TAG = "PackageParserCacheHelper"; + private static final boolean DEBUG = false; + + /** + * Parcel read helper with a string pool. + */ + public static class ReadHelper extends Parcel.ReadWriteHelper { + private final ArrayList<String> mStrings = new ArrayList<>(); + + private final Parcel mParcel; + + public ReadHelper(Parcel p) { + mParcel = p; + } + + /** + * Prepare to read from a parcel, and install itself as a read-write helper. + * + * (We don't do it in the constructor to avoid calling methods before the constructor + * finishes.) + */ + public void startAndInstall() { + mStrings.clear(); + + final int poolPosition = mParcel.readInt(); + final int startPosition = mParcel.dataPosition(); + + // The pool is at the end of the parcel. + mParcel.setDataPosition(poolPosition); + mParcel.readStringList(mStrings); + + // Then move back. + mParcel.setDataPosition(startPosition); + + if (DEBUG) { + Log.i(TAG, "Read " + mStrings.size() + " strings"); + for (int i = 0; i < mStrings.size(); i++) { + Log.i(TAG, " " + i + ": \"" + mStrings.get(i) + "\""); + } + } + + mParcel.setReadWriteHelper(this); + } + + /** + * Read an string index from a parcel, and returns the corresponding string from the pool. + */ + @Override + public String readString(Parcel p) { + return mStrings.get(p.readInt()); + } + } + + /** + * Parcel write helper with a string pool. + */ + public static class WriteHelper extends Parcel.ReadWriteHelper { + private final ArrayList<String> mStrings = new ArrayList<>(); + + private final HashMap<String, Integer> mIndexes = new HashMap<>(); + + private final Parcel mParcel; + private final int mStartPos; + + /** + * Constructor. Prepare a parcel, and install it self as a read-write helper. + */ + public WriteHelper(Parcel p) { + mParcel = p; + mStartPos = p.dataPosition(); + mParcel.writeInt(0); // We come back later here and write the pool position. + + mParcel.setReadWriteHelper(this); + } + + /** + * Instead of writing a string directly to a parcel, this method adds it to the pool, + * and write the index in the pool to the parcel. + */ + @Override + public void writeString(Parcel p, String s) { + final Integer cur = mIndexes.get(s); + if (cur != null) { + // String already in the pool. Just write the index. + p.writeInt(cur); // Already in the pool. + if (DEBUG) { + Log.i(TAG, "Duplicate '" + s + "' at " + cur); + } + } else { + // Not in the pool. Add to the pool, and write the index. + final int index = mStrings.size(); + mIndexes.put(s, index); + mStrings.add(s); + + if (DEBUG) { + Log.i(TAG, "New '" + s + "' at " + index); + } + + p.writeInt(index); + } + } + + /** + * Closes a parcel by appending the string pool at the end and updating the pool offset, + * which it assumes is at the first byte. It also uninstalls itself as a read-write helper. + */ + public void finishAndUninstall() { + // Uninstall first, so that writeStringList() uses the native writeString. + mParcel.setReadWriteHelper(null); + + final int poolPosition = mParcel.dataPosition(); + mParcel.writeStringList(mStrings); + + mParcel.setDataPosition(mStartPos); + mParcel.writeInt(poolPosition); + + // Move back to the end. + mParcel.setDataPosition(mParcel.dataSize()); + if (DEBUG) { + Log.i(TAG, "Wrote " + mStrings.size() + " strings"); + } + } + } +} diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index a5763efb034d..5312dcaeb508 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.NonNull; import android.annotation.Nullable; import android.util.ArrayMap; import android.util.Log; @@ -23,6 +24,7 @@ import android.util.MathUtils; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import java.io.Serializable; @@ -94,7 +96,8 @@ public class BaseBundle { private ClassLoader mClassLoader; /** {@hide} */ - int mFlags; + @VisibleForTesting + public int mFlags; /** * Constructs a new, empty Bundle that uses a specific ClassLoader for @@ -218,59 +221,72 @@ public class BaseBundle { */ /* package */ void unparcel() { synchronized (this) { - final Parcel parcelledData = mParcelledData; - if (parcelledData == null) { - if (DEBUG) Log.d(TAG, "unparcel " - + Integer.toHexString(System.identityHashCode(this)) - + ": no parcelled data"); - return; + final Parcel source = mParcelledData; + if (source != null) { + initializeFromParcelLocked(source, /*recycleParcel=*/ true); + } else { + if (DEBUG) { + Log.d(TAG, "unparcel " + + Integer.toHexString(System.identityHashCode(this)) + + ": no parcelled data"); + } } + } + } - if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) { - Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may " - + "clobber all data inside!", new Throwable()); - } + private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel) { + if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) { + Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may " + + "clobber all data inside!", new Throwable()); + } - if (isEmptyParcel()) { - if (DEBUG) Log.d(TAG, "unparcel " + if (isEmptyParcel(parcelledData)) { + if (DEBUG) { + Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this)) + ": empty"); - if (mMap == null) { - mMap = new ArrayMap<>(1); - } else { - mMap.erase(); - } - mParcelledData = null; - return; } - - int N = parcelledData.readInt(); - if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this)) - + ": reading " + N + " maps"); - if (N < 0) { - return; - } - ArrayMap<String, Object> map = mMap; - if (map == null) { - map = new ArrayMap<>(N); + if (mMap == null) { + mMap = new ArrayMap<>(1); } else { + mMap.erase(); + } + mParcelledData = null; + return; + } + + final int count = parcelledData.readInt(); + if (DEBUG) { + Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this)) + + ": reading " + count + " maps"); + } + if (count < 0) { + return; + } + ArrayMap<String, Object> map = mMap; + if (map == null) { + map = new ArrayMap<>(count); + } else { + map.erase(); + map.ensureCapacity(count); + } + try { + parcelledData.readArrayMapInternal(map, count, mClassLoader); + } catch (BadParcelableException e) { + if (sShouldDefuse) { + Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e); map.erase(); - map.ensureCapacity(N); + } else { + throw e; } - try { - parcelledData.readArrayMapInternal(map, N, mClassLoader); - } catch (BadParcelableException e) { - if (sShouldDefuse) { - Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e); - map.erase(); - } else { - throw e; - } - } finally { - mMap = map; - parcelledData.recycle(); - mParcelledData = null; + } finally { + mMap = map; + if (recycleParcel) { + recycleParcel(parcelledData); } - if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this)) + mParcelledData = null; + } + if (DEBUG) { + Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this)) + " final map: " + mMap); } } @@ -286,7 +302,20 @@ public class BaseBundle { * @hide */ public boolean isEmptyParcel() { - return mParcelledData == NoImagePreloadHolder.EMPTY_PARCEL; + return isEmptyParcel(mParcelledData); + } + + /** + * @hide + */ + private static boolean isEmptyParcel(Parcel p) { + return p == NoImagePreloadHolder.EMPTY_PARCEL; + } + + private static void recycleParcel(Parcel p) { + if (p != null && !isEmptyParcel(p)) { + p.recycle(); + } } /** @hide */ @@ -1476,6 +1505,10 @@ public class BaseBundle { * @param parcel The parcel to copy this bundle to. */ void writeToParcelInner(Parcel parcel, int flags) { + // If the parcel has a read-write helper, we can't just copy the blob, so unparcel it first. + if (parcel.hasReadWriteHelper()) { + unparcel(); + } // Keep implementation in sync with writeToParcel() in // frameworks/native/libs/binder/PersistableBundle.cpp. final ArrayMap<String, Object> map; @@ -1544,6 +1577,15 @@ public class BaseBundle { + Integer.toHexString(magic)); } + if (parcel.hasReadWriteHelper()) { + // If the parcel has a read-write helper, then we can't lazily-unparcel it, so just + // unparcel right away. + synchronized (this) { + initializeFromParcelLocked(parcel, /*recycleParcel=*/ false); + } + return; + } + // Advance within this Parcel int offset = parcel.dataPosition(); parcel.setDataPosition(MathUtils.addOrThrow(offset, length)); diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index cc6b5e1e3ce3..6d44330934bc 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -206,8 +206,15 @@ public abstract class BatteryStats implements Parcelable { * - CPU frequency time per uid * New in version 22: * - BLE scan result background count, BLE unoptimized scan time + * - Background partial wakelock time & count + * New in version 23: + * - Logging smeared power model values + * New in version 24: + * - Fixed bugs in background timers and BLE scan time + * New in version 25: + * - Package wakeup alarms are now on screen-off timebase */ - static final String CHECKIN_VERSION = "24"; + static final String CHECKIN_VERSION = "25"; /** * Old version, we hit 9 and ran out of room, need to remove. @@ -686,7 +693,7 @@ public abstract class BatteryStats implements Parcelable { public abstract long getSystemCpuTimeUs(int which); /** - * Returns the approximate cpu time (in milliseconds) spent at a certain CPU speed for a + * Returns the approximate cpu time (in microseconds) spent at a certain CPU speed for a * given CPU cluster. * @param cluster the index of the CPU cluster. * @param step the index of the CPU speed. This is not the actual speed of the CPU. @@ -3544,7 +3551,12 @@ public abstract class BatteryStats implements Parcelable { if (name.indexOf(',') >= 0) { name = name.replace(',', '_'); } - name = name.replaceAll("[\\n|\\r]+", ""); + if (name.indexOf('\n') >= 0) { + name = name.replace('\n', '_'); + } + if (name.indexOf('\r') >= 0) { + name = name.replace('\r', '_'); + } dumpLine(pw, uid, category, WAKELOCK_DATA, name, sb.toString()); } } diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index 51f96eed822e..c58153aa34d1 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -22,6 +22,8 @@ import android.util.Size; import android.util.SizeF; import android.util.SparseArray; +import com.android.internal.annotations.VisibleForTesting; + import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -32,9 +34,14 @@ import java.util.List; * @see PersistableBundle */ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { - private static final int FLAG_HAS_FDS = 1 << 8; - private static final int FLAG_HAS_FDS_KNOWN = 1 << 9; - private static final int FLAG_ALLOW_FDS = 1 << 10; + @VisibleForTesting + static final int FLAG_HAS_FDS = 1 << 8; + + @VisibleForTesting + static final int FLAG_HAS_FDS_KNOWN = 1 << 9; + + @VisibleForTesting + static final int FLAG_ALLOW_FDS = 1 << 10; public static final Bundle EMPTY; @@ -65,20 +72,42 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { * will be unparcelled on first contact, using the assigned ClassLoader. * * @param parcelledData a Parcel containing a Bundle + * + * @hide */ - Bundle(Parcel parcelledData) { + @VisibleForTesting + public Bundle(Parcel parcelledData) { super(parcelledData); - mFlags = FLAG_HAS_FDS_KNOWN | FLAG_ALLOW_FDS; - if (mParcelledData.hasFileDescriptors()) { - mFlags |= FLAG_HAS_FDS; - } + mFlags = FLAG_ALLOW_FDS; + maybePrefillHasFds(); } - /* package */ Bundle(Parcel parcelledData, int length) { + /** + * Constructor from a parcel for when the length is known *and is not stored in the parcel.* + * The other constructor that takes a parcel assumes the length is in the parcel. + * + * @hide + */ + @VisibleForTesting + public Bundle(Parcel parcelledData, int length) { super(parcelledData, length); - mFlags = FLAG_HAS_FDS_KNOWN | FLAG_ALLOW_FDS; - if (mParcelledData.hasFileDescriptors()) { - mFlags |= FLAG_HAS_FDS; + mFlags = FLAG_ALLOW_FDS; + maybePrefillHasFds(); + } + + /** + * If {@link #mParcelledData} is not null, copy the HAS FDS bit from it because it's fast. + * Otherwise (if {@link #mParcelledData} is already null), leave {@link #FLAG_HAS_FDS_KNOWN} + * unset, because scanning a map is slower. We'll do it lazily in + * {@link #hasFileDescriptors()}. + */ + private void maybePrefillHasFds() { + if (mParcelledData != null) { + if (mParcelledData.hasFileDescriptors()) { + mFlags |= FLAG_HAS_FDS | FLAG_HAS_FDS_KNOWN; + } else { + mFlags |= FLAG_HAS_FDS_KNOWN; + } } } @@ -1213,10 +1242,8 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { */ public void readFromParcel(Parcel parcel) { super.readFromParcelInner(parcel); - mFlags = FLAG_HAS_FDS_KNOWN | FLAG_ALLOW_FDS; - if (mParcelledData.hasFileDescriptors()) { - mFlags |= FLAG_HAS_FDS; - } + mFlags = FLAG_ALLOW_FDS; + maybePrefillHasFds(); } @Override diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 2efb0f5e419d..fae9d5310f8e 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -291,7 +291,7 @@ public final class Parcel { private static native void nativeWriteFloat(long nativePtr, float val); @FastNative private static native void nativeWriteDouble(long nativePtr, double val); - private static native void nativeWriteString(long nativePtr, String val); + static native void nativeWriteString(long nativePtr, String val); private static native void nativeWriteStrongBinder(long nativePtr, IBinder val); private static native long nativeWriteFileDescriptor(long nativePtr, FileDescriptor val); @@ -306,7 +306,7 @@ public final class Parcel { private static native float nativeReadFloat(long nativePtr); @CriticalNative private static native double nativeReadDouble(long nativePtr); - private static native String nativeReadString(long nativePtr); + static native String nativeReadString(long nativePtr); private static native IBinder nativeReadStrongBinder(long nativePtr); private static native FileDescriptor nativeReadFileDescriptor(long nativePtr); @@ -339,6 +339,33 @@ public final class Parcel { }; /** + * @hide + */ + public static class ReadWriteHelper { + public static final ReadWriteHelper DEFAULT = new ReadWriteHelper(); + + /** + * Called when writing a string to a parcel. Subclasses wanting to write a string + * must use {@link #writeStringNoHelper(String)} to avoid + * infinity recursive calls. + */ + public void writeString(Parcel p, String s) { + nativeWriteString(p.mNativePtr, s); + } + + /** + * Called when reading a string to a parcel. Subclasses wanting to read a string + * must use {@link #readStringNoHelper()} to avoid + * infinity recursive calls. + */ + public String readString(Parcel p) { + return nativeReadString(p.mNativePtr); + } + } + + private ReadWriteHelper mReadWriteHelper = ReadWriteHelper.DEFAULT; + + /** * Retrieve a new Parcel object from the pool. */ public static Parcel obtain() { @@ -352,6 +379,7 @@ public final class Parcel { if (DEBUG_RECYCLE) { p.mStack = new RuntimeException(); } + p.mReadWriteHelper = ReadWriteHelper.DEFAULT; return p; } } @@ -385,6 +413,25 @@ public final class Parcel { } } + /** + * Set a {@link ReadWriteHelper}, which can be used to avoid having duplicate strings, for + * example. + * + * @hide + */ + public void setReadWriteHelper(ReadWriteHelper helper) { + mReadWriteHelper = helper != null ? helper : ReadWriteHelper.DEFAULT; + } + + /** + * @return whether this parcel has a {@link ReadWriteHelper}. + * + * @hide + */ + public boolean hasReadWriteHelper() { + return (mReadWriteHelper != null) && (mReadWriteHelper != ReadWriteHelper.DEFAULT); + } + /** @hide */ public static native long getGlobalAllocSize(); @@ -625,6 +672,17 @@ public final class Parcel { * growing dataCapacity() if needed. */ public final void writeString(String val) { + mReadWriteHelper.writeString(this, val); + } + + /** + * Write a string without going though a {@link ReadWriteHelper}. Subclasses of + * {@link ReadWriteHelper} must use this method instead of {@link #writeString} to avoid + * infinity recursive calls. + * + * @hide + */ + public void writeStringNoHelper(String val) { nativeWriteString(mNativePtr, val); } @@ -1997,6 +2055,17 @@ public final class Parcel { * Read a string value from the parcel at the current dataPosition(). */ public final String readString() { + return mReadWriteHelper.readString(this); + } + + /** + * Read a string without going though a {@link ReadWriteHelper}. Subclasses of + * {@link ReadWriteHelper} must use this method instead of {@link #readString} to avoid + * infinity recursive calls. + * + * @hide + */ + public String readStringNoHelper() { return nativeReadString(mNativePtr); } @@ -2997,6 +3066,7 @@ public final class Parcel { if (mOwnsNativeParcelObject) { updateNativeSize(nativeFreeBuffer(mNativePtr)); } + mReadWriteHelper = ReadWriteHelper.DEFAULT; } private void destroy() { @@ -3007,6 +3077,7 @@ public final class Parcel { } mNativePtr = 0; } + mReadWriteHelper = null; } @Override diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java index 0edb154cbf9f..3da0b5e84499 100644 --- a/core/java/android/service/autofill/CustomDescription.java +++ b/core/java/android/service/autofill/CustomDescription.java @@ -32,7 +32,7 @@ import com.android.internal.util.Preconditions; * * <p>This is useful when the autofill service needs to show a detailed view of what would be saved; * for example, when the screen contains a credit card, it could display a logo of the credit card - * bank, the last for digits of the credit card number, and its expiration number. + * bank, the last four digits of the credit card number, and its expiration number. * * <p>A custom description is made of 2 parts: * <ul> @@ -63,16 +63,16 @@ import com.android.internal.util.Preconditions; * // Image child - different logo for each bank, based on credit card prefix * builder.addChild(R.id.templateccLogo, * new ImageTransformation.Builder(ccNumberId) - * .addOption("^4815.*$", R.drawable.ic_credit_card_logo1) - * .addOption("^1623.*$", R.drawable.ic_credit_card_logo2) - * .addOption("^42.*$", R.drawable.ic_credit_card_logo3); + * .addOption(Pattern.compile(""^4815.*$"), R.drawable.ic_credit_card_logo1) + * .addOption(Pattern.compile(""^1623.*$"), R.drawable.ic_credit_card_logo2) + * .addOption(Pattern.compile(""^42.*$"), R.drawable.ic_credit_card_logo3); * // Masked credit card number (as .....LAST_4_DIGITS) * builder.addChild(R.id.templateCcNumber, new CharSequenceTransformation.Builder() - * .addField(ccNumberId, "^.*(\\d\\d\\d\\d)$", "...$1") + * .addField(ccNumberId, Pattern.compile(""^.*(\\d\\d\\d\\d)$"), "...$1") * // Expiration date as MM / YYYY: * builder.addChild(R.id.templateExpDate, new CharSequenceTransformation.Builder() - * .addField(ccExpMonthId, "^(\\d\\d)$", "Exp: $1") - * .addField(ccExpYearId, "^(\\d\\d)$", "/$1"); + * .addField(ccExpMonthId, Pattern.compile(""^(\\d\\d)$"), "Exp: $1") + * .addField(ccExpYearId, Pattern.compile(""^(\\d\\d)$"), "/$1"); * </pre> * * <p>See {@link ImageTransformation}, {@link CharSequenceTransformation} for more info about these diff --git a/core/java/android/service/autofill/SimpleRegexValidator.java b/core/java/android/service/autofill/RegexValidator.java index ef8c52c908b3..9dfe78d2b372 100644 --- a/core/java/android/service/autofill/SimpleRegexValidator.java +++ b/core/java/android/service/autofill/RegexValidator.java @@ -34,9 +34,9 @@ import java.util.regex.Pattern; * * <p>See {@link SaveInfo.Builder#setValidator(Validator)} for examples. */ -public final class SimpleRegexValidator extends InternalValidator implements Validator, Parcelable { +public final class RegexValidator extends InternalValidator implements Validator, Parcelable { - private static final String TAG = "SimpleRegexValidator"; + private static final String TAG = "RegexValidator"; private final AutofillId mId; private final Pattern mRegex; @@ -49,7 +49,7 @@ public final class SimpleRegexValidator extends InternalValidator implements Val * matches the contents of the field identified by {@code id}, it returns {@code true}; * otherwise, it returns {@code false}. */ - public SimpleRegexValidator(@NonNull AutofillId id, @NonNull Pattern regex) { + public RegexValidator(@NonNull AutofillId id, @NonNull Pattern regex) { mId = Preconditions.checkNotNull(id); mRegex = Preconditions.checkNotNull(regex); } @@ -76,7 +76,7 @@ public final class SimpleRegexValidator extends InternalValidator implements Val public String toString() { if (!sDebug) return super.toString(); - return "SimpleRegexValidator: [id=" + mId + ", regex=" + mRegex + "]"; + return "RegexValidator: [id=" + mId + ", regex=" + mRegex + "]"; } ///////////////////////////////////// @@ -93,17 +93,17 @@ public final class SimpleRegexValidator extends InternalValidator implements Val parcel.writeSerializable(mRegex); } - public static final Parcelable.Creator<SimpleRegexValidator> CREATOR = - new Parcelable.Creator<SimpleRegexValidator>() { + public static final Parcelable.Creator<RegexValidator> CREATOR = + new Parcelable.Creator<RegexValidator>() { @Override - public SimpleRegexValidator createFromParcel(Parcel parcel) { - return new SimpleRegexValidator(parcel.readParcelable(null), + public RegexValidator createFromParcel(Parcel parcel) { + return new RegexValidator(parcel.readParcelable(null), (Pattern) parcel.readSerializable()); } @Override - public SimpleRegexValidator[] newArray(int size) { - return new SimpleRegexValidator[size]; + public RegexValidator[] newArray(int size) { + return new RegexValidator[size]; } }; } diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java index f8a94d6ce467..e0a073050b6b 100644 --- a/core/java/android/service/autofill/SaveInfo.java +++ b/core/java/android/service/autofill/SaveInfo.java @@ -470,7 +470,7 @@ public final class SaveInfo implements Parcelable { * <p>Validator for a credit number that must have exactly 16 digits: * * <pre class="prettyprint"> - * Validator validator = new SimpleRegexValidator(ccNumberId, "^\\d{16}$") + * Validator validator = new RegexValidator(ccNumberId, Pattern.compile(""^\\d{16}$")) * </pre> * * <p>Validator for a credit number that must pass a Luhn checksum and either have @@ -483,8 +483,8 @@ public final class SaveInfo implements Parcelable { * and( * new LuhnChecksumValidator(ccNumberId), * or( - * new SimpleRegexValidator(ccNumberId, "^\\d{16}$"), - * new SimpleRegexValidator(ccNumberId, "^108\\d{12}$") + * new RegexValidator(ccNumberId, Pattern.compile(""^\\d{16}$")), + * new RegexValidator(ccNumberId, Pattern.compile(""^108\\d{12}$")) * ) * ); * </pre> @@ -496,7 +496,7 @@ public final class SaveInfo implements Parcelable { * Validator validator = * and( * new LuhnChecksumValidator(ccNumberId), - * new SimpleRegexValidator(ccNumberId, "^(\\d{16}|108\\d{12})$") + * new RegexValidator(ccNumberId, Pattern.compile(""^(\\d{16}|108\\d{12})$")) * ); * </pre> * @@ -508,10 +508,10 @@ public final class SaveInfo implements Parcelable { * * Validator validator = * and( - * new SimpleRegexValidator(ccNumberId1, "^\\d{4}$"), - * new SimpleRegexValidator(ccNumberId2, "^\\d{4}$"), - * new SimpleRegexValidator(ccNumberId3, "^\\d{4}$"), - * new SimpleRegexValidator(ccNumberId4, "^\\d{4}$") + * new RegexValidator(ccNumberId1, Pattern.compile(""^\\d{4}$")), + * new RegexValidator(ccNumberId2, Pattern.compile(""^\\d{4}$")), + * new RegexValidator(ccNumberId3, Pattern.compile(""^\\d{4}$")), + * new RegexValidator(ccNumberId4, Pattern.compile(""^\\d{4}$")) * ); * </pre> * diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 44c88e146879..1a2968f6345f 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -65,7 +65,8 @@ public class SurfaceControl { private static native void nativeSetSize(long nativeObject, int w, int h); private static native void nativeSetTransparentRegionHint(long nativeObject, Region region); private static native void nativeSetAlpha(long nativeObject, float alpha); - private static native void nativeSetMatrix(long nativeObject, float dsdx, float dtdx, float dsdy, float dtdy); + private static native void nativeSetMatrix(long nativeObject, float dsdx, float dtdx, + float dtdy, float dsdy); private static native void nativeSetFlags(long nativeObject, int flags, int mask); private static native void nativeSetWindowCrop(long nativeObject, int l, int t, int r, int b); private static native void nativeSetFinalCrop(long nativeObject, int l, int t, int r, int b); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index e59cf8408963..e2bcea0669b2 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -119,7 +119,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 162 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 163 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS; @@ -5707,7 +5707,7 @@ public class BatteryStatsImpl extends BatteryStats { LongSamplingCounter mUserCpuTime; LongSamplingCounter mSystemCpuTime; - LongSamplingCounter[][] mCpuClusterSpeed; + LongSamplingCounter[][] mCpuClusterSpeedTimesUs; LongSamplingCounterArray mCpuFreqTimeMs; LongSamplingCounterArray mScreenOffCpuFreqTimeMs; @@ -6556,12 +6556,12 @@ public class BatteryStatsImpl extends BatteryStats { @Override public long getTimeAtCpuSpeed(int cluster, int step, int which) { - if (mCpuClusterSpeed != null) { - if (cluster >= 0 && cluster < mCpuClusterSpeed.length) { - final LongSamplingCounter[] cpuSpeeds = mCpuClusterSpeed[cluster]; - if (cpuSpeeds != null) { - if (step >= 0 && step < cpuSpeeds.length) { - final LongSamplingCounter c = cpuSpeeds[step]; + if (mCpuClusterSpeedTimesUs != null) { + if (cluster >= 0 && cluster < mCpuClusterSpeedTimesUs.length) { + final LongSamplingCounter[] cpuSpeedTimesUs = mCpuClusterSpeedTimesUs[cluster]; + if (cpuSpeedTimesUs != null) { + if (step >= 0 && step < cpuSpeedTimesUs.length) { + final LongSamplingCounter c = cpuSpeedTimesUs[step]; if (c != null) { return c.getCountLocked(which); } @@ -6712,8 +6712,8 @@ public class BatteryStatsImpl extends BatteryStats { mUserCpuTime.reset(false); mSystemCpuTime.reset(false); - if (mCpuClusterSpeed != null) { - for (LongSamplingCounter[] speeds : mCpuClusterSpeed) { + if (mCpuClusterSpeedTimesUs != null) { + for (LongSamplingCounter[] speeds : mCpuClusterSpeedTimesUs) { if (speeds != null) { for (LongSamplingCounter speed : speeds) { if (speed != null) { @@ -6902,8 +6902,8 @@ public class BatteryStatsImpl extends BatteryStats { mUserCpuTime.detach(); mSystemCpuTime.detach(); - if (mCpuClusterSpeed != null) { - for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) { + if (mCpuClusterSpeedTimesUs != null) { + for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeedTimesUs) { if (cpuSpeeds != null) { for (LongSamplingCounter c : cpuSpeeds) { if (c != null) { @@ -7156,10 +7156,10 @@ public class BatteryStatsImpl extends BatteryStats { mUserCpuTime.writeToParcel(out); mSystemCpuTime.writeToParcel(out); - if (mCpuClusterSpeed != null) { + if (mCpuClusterSpeedTimesUs != null) { out.writeInt(1); - out.writeInt(mCpuClusterSpeed.length); - for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) { + out.writeInt(mCpuClusterSpeedTimesUs.length); + for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeedTimesUs) { if (cpuSpeeds != null) { out.writeInt(1); out.writeInt(cpuSpeeds.length); @@ -7453,7 +7453,7 @@ public class BatteryStatsImpl extends BatteryStats { throw new ParcelFormatException("Incompatible number of cpu clusters"); } - mCpuClusterSpeed = new LongSamplingCounter[numCpuClusters][]; + mCpuClusterSpeedTimesUs = new LongSamplingCounter[numCpuClusters][]; for (int cluster = 0; cluster < numCpuClusters; cluster++) { if (in.readInt() != 0) { int numSpeeds = in.readInt(); @@ -7463,18 +7463,19 @@ public class BatteryStatsImpl extends BatteryStats { } final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds]; - mCpuClusterSpeed[cluster] = cpuSpeeds; + mCpuClusterSpeedTimesUs[cluster] = cpuSpeeds; for (int speed = 0; speed < numSpeeds; speed++) { if (in.readInt() != 0) { - cpuSpeeds[speed] = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in); + cpuSpeeds[speed] = new LongSamplingCounter( + mBsi.mOnBatteryTimeBase, in); } } } else { - mCpuClusterSpeed[cluster] = null; + mCpuClusterSpeedTimesUs[cluster] = null; } } } else { - mCpuClusterSpeed = null; + mCpuClusterSpeedTimesUs = null; } mCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(in, mBsi.mOnBatteryTimeBase); @@ -8043,6 +8044,7 @@ public class BatteryStatsImpl extends BatteryStats { /** * Number of times wakeup alarms have occurred for this app. + * On screen-off timebase starting in report v25. */ ArrayMap<String, Counter> mWakeupAlarms = new ArrayMap<>(); @@ -8071,7 +8073,7 @@ public class BatteryStatsImpl extends BatteryStats { mWakeupAlarms.clear(); for (int i=0; i<numWA; i++) { String tag = in.readString(); - mWakeupAlarms.put(tag, new Counter(mBsi.mOnBatteryTimeBase, in)); + mWakeupAlarms.put(tag, new Counter(mBsi.mOnBatteryScreenOffTimeBase, in)); } int numServs = in.readInt(); @@ -8110,7 +8112,7 @@ public class BatteryStatsImpl extends BatteryStats { public void noteWakeupAlarmLocked(String tag) { Counter c = mWakeupAlarms.get(tag); if (c == null) { - c = new Counter(mBsi.mOnBatteryTimeBase); + c = new Counter(mBsi.mOnBatteryScreenOffTimeBase); mWakeupAlarms.put(tag, c); } c.stepAtomic(); @@ -10429,46 +10431,48 @@ public class BatteryStatsImpl extends BatteryStats { } } - long totalCpuClustersTime = 0; + long totalCpuClustersTimeMs = 0; // Read the time spent for each cluster at various cpu frequencies. - final long[][] clusterSpeeds = new long[mKernelCpuSpeedReaders.length][]; + final long[][] clusterSpeedTimesMs = new long[mKernelCpuSpeedReaders.length][]; for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) { - clusterSpeeds[cluster] = mKernelCpuSpeedReaders[cluster].readDelta(); - if (clusterSpeeds[cluster] != null) { - for (int speed = clusterSpeeds[cluster].length - 1; speed >= 0; --speed) { - totalCpuClustersTime += clusterSpeeds[cluster][speed]; + clusterSpeedTimesMs[cluster] = mKernelCpuSpeedReaders[cluster].readDelta(); + if (clusterSpeedTimesMs[cluster] != null) { + for (int speed = clusterSpeedTimesMs[cluster].length - 1; speed >= 0; --speed) { + totalCpuClustersTimeMs += clusterSpeedTimesMs[cluster][speed]; } } } - if (totalCpuClustersTime != 0) { + if (totalCpuClustersTimeMs != 0) { // We have cpu times per freq aggregated over all uids but we need the times per uid. // So, we distribute total time spent by an uid to different cpu freqs based on the // amount of time cpu was running at that freq. final int updatedUidsCount = updatedUids.size(); for (int i = 0; i < updatedUidsCount; ++i) { final Uid u = getUidStatsLocked(updatedUids.keyAt(i)); - final long appCpuTimeMs = updatedUids.valueAt(i) / 1000; + final long appCpuTimeUs = updatedUids.valueAt(i); // Add the cpu speeds to this UID. final int numClusters = mPowerProfile.getNumCpuClusters(); - if (u.mCpuClusterSpeed == null || u.mCpuClusterSpeed.length != + if (u.mCpuClusterSpeedTimesUs == null || u.mCpuClusterSpeedTimesUs.length != numClusters) { - u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][]; + u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][]; } - for (int cluster = 0; cluster < clusterSpeeds.length; cluster++) { - final int speedsInCluster = clusterSpeeds[cluster].length; - if (u.mCpuClusterSpeed[cluster] == null || speedsInCluster != - u.mCpuClusterSpeed[cluster].length) { - u.mCpuClusterSpeed[cluster] = new LongSamplingCounter[speedsInCluster]; + for (int cluster = 0; cluster < clusterSpeedTimesMs.length; cluster++) { + final int speedsInCluster = clusterSpeedTimesMs[cluster].length; + if (u.mCpuClusterSpeedTimesUs[cluster] == null || speedsInCluster != + u.mCpuClusterSpeedTimesUs[cluster].length) { + u.mCpuClusterSpeedTimesUs[cluster] + = new LongSamplingCounter[speedsInCluster]; } - final LongSamplingCounter[] cpuSpeeds = u.mCpuClusterSpeed[cluster]; + final LongSamplingCounter[] cpuSpeeds = u.mCpuClusterSpeedTimesUs[cluster]; for (int speed = 0; speed < speedsInCluster; speed++) { if (cpuSpeeds[speed] == null) { cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase); } - cpuSpeeds[speed].addCountLocked(appCpuTimeMs * clusterSpeeds[cluster][speed] - / totalCpuClustersTime); + cpuSpeeds[speed].addCountLocked(appCpuTimeUs + * clusterSpeedTimesMs[cluster][speed] + / totalCpuClustersTimeMs); } } } @@ -11752,7 +11756,7 @@ public class BatteryStatsImpl extends BatteryStats { throw new ParcelFormatException("Incompatible cpu cluster arrangement"); } - u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][]; + u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][]; for (int cluster = 0; cluster < numClusters; cluster++) { if (in.readInt() != 0) { final int NSB = in.readInt(); @@ -11762,20 +11766,20 @@ public class BatteryStatsImpl extends BatteryStats { NSB); } - u.mCpuClusterSpeed[cluster] = new LongSamplingCounter[NSB]; + u.mCpuClusterSpeedTimesUs[cluster] = new LongSamplingCounter[NSB]; for (int speed = 0; speed < NSB; speed++) { if (in.readInt() != 0) { - u.mCpuClusterSpeed[cluster][speed] = new LongSamplingCounter( + u.mCpuClusterSpeedTimesUs[cluster][speed] = new LongSamplingCounter( mOnBatteryTimeBase); - u.mCpuClusterSpeed[cluster][speed].readSummaryFromParcelLocked(in); + u.mCpuClusterSpeedTimesUs[cluster][speed].readSummaryFromParcelLocked(in); } } } else { - u.mCpuClusterSpeed[cluster] = null; + u.mCpuClusterSpeedTimesUs[cluster] = null; } } } else { - u.mCpuClusterSpeed = null; + u.mCpuClusterSpeedTimesUs = null; } u.mCpuFreqTimeMs = LongSamplingCounterArray.readSummaryFromParcelLocked( @@ -11867,7 +11871,7 @@ public class BatteryStatsImpl extends BatteryStats { p.mWakeupAlarms.clear(); for (int iwa=0; iwa<NWA; iwa++) { String tag = in.readString(); - Counter c = new Counter(mOnBatteryTimeBase); + Counter c = new Counter(mOnBatteryScreenOffTimeBase); c.readSummaryFromParcelLocked(in); p.mWakeupAlarms.put(tag, c); } @@ -12183,10 +12187,10 @@ public class BatteryStatsImpl extends BatteryStats { u.mUserCpuTime.writeSummaryFromParcelLocked(out); u.mSystemCpuTime.writeSummaryFromParcelLocked(out); - if (u.mCpuClusterSpeed != null) { + if (u.mCpuClusterSpeedTimesUs != null) { out.writeInt(1); - out.writeInt(u.mCpuClusterSpeed.length); - for (LongSamplingCounter[] cpuSpeeds : u.mCpuClusterSpeed) { + out.writeInt(u.mCpuClusterSpeedTimesUs.length); + for (LongSamplingCounter[] cpuSpeeds : u.mCpuClusterSpeedTimesUs) { if (cpuSpeeds != null) { out.writeInt(1); out.writeInt(cpuSpeeds.length); diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java index 9d52e19c73bf..bb743c157d1f 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -22,6 +22,7 @@ import android.util.Log; public class CpuPowerCalculator extends PowerCalculator { private static final String TAG = "CpuPowerCalculator"; private static final boolean DEBUG = BatteryStatsHelper.DEBUG; + private static final long MICROSEC_IN_HR = (long) 60 * 60 * 1000 * 1000; private final PowerProfile mProfile; public CpuPowerCalculator(PowerProfile profile) { @@ -35,21 +36,22 @@ public class CpuPowerCalculator extends PowerCalculator { app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000; final int numClusters = mProfile.getNumCpuClusters(); - double cpuPowerMaMs = 0; + double cpuPowerMaUs = 0; for (int cluster = 0; cluster < numClusters; cluster++) { final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster); for (int speed = 0; speed < speedsForCluster; speed++) { - final double cpuSpeedStepPower = u.getTimeAtCpuSpeed(cluster, speed, statsType) * + final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType); + final double cpuSpeedStepPower = timeUs * mProfile.getAveragePowerForCpu(cluster, speed); if (DEBUG) { Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #" - + speed + " power=" - + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000))); + + speed + " timeUs=" + timeUs + " power=" + + BatteryStatsHelper.makemAh(cpuSpeedStepPower / MICROSEC_IN_HR)); } - cpuPowerMaMs += cpuSpeedStepPower; + cpuPowerMaUs += cpuSpeedStepPower; } } - app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000); + app.cpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR; if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) { Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + app.cpuTimeMs + " ms power=" diff --git a/core/java/com/android/internal/os/KernelCpuSpeedReader.java b/core/java/com/android/internal/os/KernelCpuSpeedReader.java index 9c7debb7d9c9..757a1121acf5 100644 --- a/core/java/com/android/internal/os/KernelCpuSpeedReader.java +++ b/core/java/com/android/internal/os/KernelCpuSpeedReader.java @@ -39,8 +39,8 @@ public class KernelCpuSpeedReader { private static final String TAG = "KernelCpuSpeedReader"; private final String mProcFile; - private final long[] mLastSpeedTimes; - private final long[] mDeltaSpeedTimes; + private final long[] mLastSpeedTimesMs; + private final long[] mDeltaSpeedTimesMs; // How long a CPU jiffy is in milliseconds. private final long mJiffyMillis; @@ -51,8 +51,8 @@ public class KernelCpuSpeedReader { public KernelCpuSpeedReader(int cpuNumber, int numSpeedSteps) { mProcFile = String.format("/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", cpuNumber); - mLastSpeedTimes = new long[numSpeedSteps]; - mDeltaSpeedTimes = new long[numSpeedSteps]; + mLastSpeedTimesMs = new long[numSpeedSteps]; + mDeltaSpeedTimesMs = new long[numSpeedSteps]; long jiffyHz = Libcore.os.sysconf(OsConstants._SC_CLK_TCK); mJiffyMillis = 1000/jiffyHz; } @@ -68,27 +68,27 @@ public class KernelCpuSpeedReader { TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' '); String line; int speedIndex = 0; - while (speedIndex < mLastSpeedTimes.length && (line = reader.readLine()) != null) { + while (speedIndex < mLastSpeedTimesMs.length && (line = reader.readLine()) != null) { splitter.setString(line); - Long.parseLong(splitter.next()); + splitter.next(); long time = Long.parseLong(splitter.next()) * mJiffyMillis; - if (time < mLastSpeedTimes[speedIndex]) { + if (time < mLastSpeedTimesMs[speedIndex]) { // The stats reset when the cpu hotplugged. That means that the time // we read is offset from 0, so the time is the delta. - mDeltaSpeedTimes[speedIndex] = time; + mDeltaSpeedTimesMs[speedIndex] = time; } else { - mDeltaSpeedTimes[speedIndex] = time - mLastSpeedTimes[speedIndex]; + mDeltaSpeedTimesMs[speedIndex] = time - mLastSpeedTimesMs[speedIndex]; } - mLastSpeedTimes[speedIndex] = time; + mLastSpeedTimesMs[speedIndex] = time; speedIndex++; } } catch (IOException e) { Slog.e(TAG, "Failed to read cpu-freq: " + e.getMessage()); - Arrays.fill(mDeltaSpeedTimes, 0); + Arrays.fill(mDeltaSpeedTimesMs, 0); } finally { StrictMode.setThreadPolicy(policy); } - return mDeltaSpeedTimes; + return mDeltaSpeedTimesMs; } } diff --git a/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml b/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml index 4b01736d67c4..2c430e08c16a 100644 --- a/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml +++ b/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml @@ -40,15 +40,31 @@ <application android:hasCode="true"> + <meta-data android:name="key1" android:value="value1" /> + <meta-data android:name="key2" android:value="this_is_app" /> + <activity android:name="com.android.frameworks.coretests.TestActivity"> + <meta-data android:name="key1" android:value="value1" /> + <meta-data android:name="key2" android:value="this_is_activity" /> </activity> <provider android:name="com.android.frameworks.coretests.TestProvider" - android:authorities="com.android.frameworks.coretests.testprovider" /> + android:authorities="com.android.frameworks.coretests.testprovider" > + <meta-data android:name="key1" android:value="value1" /> + <meta-data android:name="key2" android:value="this_is_provider" /> + </provider> + <receiver - android:name="com.android.frameworks.coretests.TestReceiver" /> + android:name="com.android.frameworks.coretests.TestReceiver" > + <meta-data android:name="key1" android:value="value1" /> + <meta-data android:name="key2" android:value="this_is_receiver" /> + </receiver> + <service - android:name="com.android.frameworks.coretests.TestService" /> + android:name="com.android.frameworks.coretests.TestService" > + <meta-data android:name="key1" android:value="value1" /> + <meta-data android:name="key2" android:value="this_is_service" /> + </service> </application> </manifest> diff --git a/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java b/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java new file mode 100644 index 000000000000..00be82219e4f --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java @@ -0,0 +1,69 @@ +/* + * 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. + */ + +package android.content.pm; + +import static org.junit.Assert.assertEquals; + +import android.content.pm.PackageParserCacheHelper.ReadHelper; +import android.content.pm.PackageParserCacheHelper.WriteHelper; +import android.os.Bundle; +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class PackageParserCacheHelperTest { + @Test + public void testParcelUnparcel() throws Exception { + final Bundle source = new Bundle(); + source.putInt("i1", 123); + source.putString("s1", "abcdef"); + source.putString("s2", "xyz"); + source.putString("s3", null); + + final Bundle nest = new Bundle(); + nest.putString("s1", "xyz"); + source.putBundle("b1", nest); + + final Parcel p = Parcel.obtain(); + final WriteHelper writeHelper = new WriteHelper(p); + + source.writeToParcel(p, 0); + writeHelper.finishAndUninstall(); + + p.setDataPosition(0); + + final ReadHelper readHelper = new ReadHelper(p); + readHelper.startAndInstall(); + + final Bundle dest = new Bundle(); + dest.readFromParcel(p); + + dest.size(); // Unparcel so that toString() returns the content. + + assertEquals(source.get("i1"), dest.get("i1")); + assertEquals(source.get("s1"), dest.get("s1")); + assertEquals(source.get("s2"), dest.get("s2")); + assertEquals(source.get("s3"), dest.get("s3")); + assertEquals(source.getBundle("b1").get("s1"), dest.getBundle("b1").get("s1")); + assertEquals(source.keySet().size(), dest.keySet().size()); + } +} diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java index 73c153f2754a..fda0f1e74543 100644 --- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java +++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java @@ -26,6 +26,7 @@ import android.content.pm.PackageParser.Component; import android.content.pm.PackageParser.Package; import android.content.pm.PackageParser.Permission; import android.os.Build; +import android.os.Bundle; import android.os.FileUtils; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; @@ -39,6 +40,7 @@ import org.junit.runner.RunWith; import java.io.File; import java.io.InputStream; import java.util.Arrays; +import java.util.function.Function; @SmallTest @RunWith(AndroidJUnit4.class) @@ -271,22 +273,27 @@ public class PackageParserTest { assertEquals(0x0083, finalConfigChanges); // Should be 10000011. } + Package parsePackage(String apkFileName, int apkResourceId) throws Exception { + return parsePackage(apkFileName, apkResourceId, p -> p); + } + /** * Attempts to parse a package. * * APKs are put into coretests/apks/packageparser_*. * - * @param apkName temporary file name to store apk extracted from resources + * @param apkFileName temporary file name to store apk extracted from resources * @param apkResourceId identifier of the apk as a resource */ - Package parsePackage(String apkFileName, int apkResourceId) throws Exception { + Package parsePackage(String apkFileName, int apkResourceId, + Function<Package, Package> converter) throws Exception { // Copy the resource to a file. Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); File outFile = new File(context.getFilesDir(), apkFileName); try { InputStream is = context.getResources().openRawResource(apkResourceId); assertTrue(FileUtils.copyToFile(is, outFile)); - return new PackageParser().parsePackage(outFile, 0 /* flags */); + return converter.apply(new PackageParser().parsePackage(outFile, 0 /* flags */)); } finally { outFile.delete(); } @@ -331,10 +338,39 @@ public class PackageParserTest { assertEquals(protectionLevel, permission.info.protectionLevel); } + private void assertMetadata(Bundle b, String... keysAndValues) { + assertTrue("Odd number of elements in keysAndValues", (keysAndValues.length % 2) == 0); + + assertNotNull(b); + assertEquals(keysAndValues.length / 2, b.size()); + + for (int i = 0; i < keysAndValues.length; i += 2) { + final String key = keysAndValues[i]; + final String value = keysAndValues[i + 1]; + + assertEquals(value, b.getString(key)); + } + } + + // TODO Add a "_cached" test for testMultiPackageComponents() too, after fixing b/64295061. + // Package.writeToParcel can't handle circular package references. + + @Test + public void testPackageWithComponents_no_cache() throws Exception { + checkPackageWithComponents(p -> p); + } + @Test - public void testPackageWithComponents() throws Exception { + public void testPackageWithComponents_cached() throws Exception { + checkPackageWithComponents(p -> + PackageParser.fromCacheEntryStatic(PackageParser.toCacheEntryStatic(p))); + } + + private void checkPackageWithComponents( + Function<Package, Package> converter) throws Exception { Package p = parsePackage( - "install_complete_package_info.apk", R.raw.install_complete_package_info); + "install_complete_package_info.apk", R.raw.install_complete_package_info, + converter); String packageName = "com.android.frameworks.coretests.install_complete_package_info"; assertEquals(packageName, p.packageName); @@ -344,6 +380,22 @@ public class PackageParserTest { packageName, PermissionInfo.PROTECTION_NORMAL, p.permissions.get(0)); assertOneComponentOfEachType("com.android.frameworks.coretests.Test%s", p); + + assertMetadata(p.mAppMetaData, + "key1", "value1", + "key2", "this_is_app"); + assertMetadata(p.activities.get(0).metaData, + "key1", "value1", + "key2", "this_is_activity"); + assertMetadata(p.services.get(0).metaData, + "key1", "value1", + "key2", "this_is_service"); + assertMetadata(p.receivers.get(0).metaData, + "key1", "value1", + "key2", "this_is_receiver"); + assertMetadata(p.providers.get(0).metaData, + "key1", "value1", + "key2", "this_is_provider"); } @Test diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java new file mode 100644 index 000000000000..9fcf96d6f3ae --- /dev/null +++ b/core/tests/coretests/src/android/os/BundleTest.java @@ -0,0 +1,140 @@ +/* + * 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. + */ + +package android.os; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Unit tests for bundle that requires accessing hidden APS. Tests that can be written only with + * public APIs should go in the CTS counterpart. + * + * Run with: + * bit FrameworksCoreTests:android.os.BundleTest + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BundleTest { + /** + * Create a test bundle, parcel it and return the parcel. + */ + private Parcel createBundleParcel(boolean withFd) throws Exception { + final Bundle source = new Bundle(); + source.putString("string", "abc"); + source.putInt("int", 1); + if (withFd) { + ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); + pipe[1].close(); + source.putParcelable("fd", pipe[0]); + } + final Parcel p = Parcel.obtain(); + // Don't use p.writeParcelabe(), which would write the creator, which we don't need. + source.writeToParcel(p, 0); + p.setDataPosition(0); + + return p; + } + + /** + * Verify a bundle generated by {@link #createBundleParcel(boolean)}. + */ + private void checkBundle(Bundle b, boolean withFd) { + // First, do the checks without actually unparceling the bundle. + // (Note looking into the contents will unparcel a bundle, so we'll do it later.) + assertTrue("mParcelledData shouldn't be null here.", b.isParcelled()); + + // Make sure FLAG_HAS_FDS and FLAG_HAS_FDS_KNOWN are set/cleared properly. + if (withFd) { + // FLAG_HAS_FDS and FLAG_HAS_FDS_KNOWN should both be set. + assertEquals(Bundle.FLAG_HAS_FDS | Bundle.FLAG_HAS_FDS_KNOWN, + b.mFlags & (Bundle.FLAG_HAS_FDS | Bundle.FLAG_HAS_FDS_KNOWN)); + } else { + // FLAG_HAS_FDS_KNOWN should be set, bot not FLAG_HAS_FDS. + assertEquals(Bundle.FLAG_HAS_FDS_KNOWN, + b.mFlags & (Bundle.FLAG_HAS_FDS | Bundle.FLAG_HAS_FDS_KNOWN)); + } + + // Then, check the contents. + assertEquals("abc", b.getString("string")); + assertEquals(1, b.getInt("int")); + + // Make sure FLAG_HAS_FDS and FLAG_HAS_FDS_KNOWN are set/cleared properly. + if (withFd) { + assertEquals(ParcelFileDescriptor.class, b.getParcelable("fd").getClass()); + assertEquals(3, b.keySet().size()); + } else { + assertEquals(2, b.keySet().size()); + } + assertFalse(b.isParcelled()); + } + + @Test + public void testCreateFromParcel() throws Exception { + boolean withFd; + Parcel p; + Bundle b; + int length; + + withFd = false; + + // new Bundle with p + p = createBundleParcel(withFd); + checkBundle(new Bundle(p), withFd); + p.recycle(); + + // new Bundle with p and length + p = createBundleParcel(withFd); + length = p.readInt(); + checkBundle(new Bundle(p, length), withFd); + p.recycle(); + + // readFromParcel() + p = createBundleParcel(withFd); + b = new Bundle(); + b.readFromParcel(p); + checkBundle(b, withFd); + p.recycle(); + + // Same test with FDs. + withFd = true; + + // new Bundle with p + p = createBundleParcel(withFd); + checkBundle(new Bundle(p), withFd); + p.recycle(); + + // new Bundle with p and length + p = createBundleParcel(withFd); + length = p.readInt(); + checkBundle(new Bundle(p, length), withFd); + p.recycle(); + + // readFromParcel() + p = createBundleParcel(withFd); + b = new Bundle(); + b.readFromParcel(p); + checkBundle(b, withFd); + p.recycle(); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java index 8fd4e3110569..0294095cca9e 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java @@ -488,7 +488,7 @@ public class BatteryStatsSensorTest extends TestCase { bi.noteStartSensorLocked(UID, SENSOR_ID); clocks.realtime += 111; - clocks.uptime += 1111; + clocks.uptime += 111; // Timebase and timer was on so times have increased. assertEquals(111_000, timer.getTotalTimeLocked(1000*clocks.realtime, which)); diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index 666445685831..4ea4e3810e03 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -16,6 +16,7 @@ package android.media; +import android.annotation.IntDef; import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; @@ -27,6 +28,8 @@ import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Map; @@ -249,7 +252,7 @@ public class MediaMetadataRetriever * @return A Bitmap containing a representative video frame, which * can be null, if such a frame cannot be retrieved. */ - public Bitmap getFrameAtTime(long timeUs, int option) { + public Bitmap getFrameAtTime(long timeUs, @Option int option) { if (option < OPTION_PREVIOUS_SYNC || option > OPTION_CLOSEST) { throw new IllegalArgumentException("Unsupported option: " + option); @@ -286,27 +289,27 @@ public class MediaMetadataRetriever * {@link #OPTION_CLOSEST} often has larger performance overhead compared * to the other options if there is no sync frame located at timeUs. * - * @param dst_width expected output bitmap width - * @param dst_height expected output bitmap height - * @return A Bitmap of size not larger than dst_width by dst_height containing a + * @param dstWidth expected output bitmap width + * @param dstHeight expected output bitmap height + * @return A Bitmap of size not larger than dstWidth by dstHeight containing a * scaled video frame, which can be null, if such a frame cannot be retrieved. * @throws IllegalArgumentException if passed in invalid option or width by height * is less than or equal to 0. */ public Bitmap getScaledFrameAtTime( - long timeUs, int option, int dst_width, int dst_height) { + long timeUs, @Option int option, int dstWidth, int dstHeight) { if (option < OPTION_PREVIOUS_SYNC || option > OPTION_CLOSEST) { throw new IllegalArgumentException("Unsupported option: " + option); } - if (dst_width <= 0) { - throw new IllegalArgumentException("Invalid width: " + dst_width); + if (dstWidth <= 0) { + throw new IllegalArgumentException("Invalid width: " + dstWidth); } - if (dst_height <= 0) { - throw new IllegalArgumentException("Invalid height: " + dst_height); + if (dstHeight <= 0) { + throw new IllegalArgumentException("Invalid height: " + dstHeight); } - return _getFrameAtTime(timeUs, option, dst_width, dst_height); + return _getFrameAtTime(timeUs, option, dstWidth, dstHeight); } /** @@ -427,6 +430,16 @@ public class MediaMetadataRetriever */ public static final int OPTION_CLOSEST = 0x03; + /** @hide */ + @IntDef(flag = true, prefix = { "OPTION_" }, value = { + OPTION_PREVIOUS_SYNC, + OPTION_NEXT_SYNC, + OPTION_CLOSEST_SYNC, + OPTION_CLOSEST, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Option {} + /* * Do not change these metadata key values without updating their * counterparts in include/media/mediametadataretriever.h! diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 472e30c7f6da..188f21600925 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -3714,6 +3714,9 @@ public class NotificationStackScrollLayout extends ViewGroup * See {@link AmbientState#setDark}. */ public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) { + if (mAmbientState.isDark() == dark) { + return; + } mAmbientState.setDark(dark); if (animate && mAnimationsEnabled) { mDarkNeedsAnimation = true; diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 814e4be28ffa..c23757fca381 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -214,8 +214,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Context.BIND_AUTO_CREATE | Context.BIND_NOT_VISIBLE | Context.BIND_NOT_FOREGROUND - | Context.BIND_IMPORTANT_BACKGROUND - | Context.BIND_SHOWING_UI; + | Context.BIND_IMPORTANT_BACKGROUND; /** * Binding flags used only while the {@link InputMethodService} is showing window. @@ -223,7 +222,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private static final int IME_VISIBLE_BIND_FLAGS = Context.BIND_AUTO_CREATE | Context.BIND_TREAT_LIKE_ACTIVITY - | Context.BIND_FOREGROUND_SERVICE; + | Context.BIND_FOREGROUND_SERVICE + | Context.BIND_SHOWING_UI; @Retention(SOURCE) @IntDef({HardKeyboardBehavior.WIRELESS_AFFORDANCE, HardKeyboardBehavior.WIRED_AFFORDANCE}) diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java index 80d39f585c12..13e3ae60f1bf 100644 --- a/services/core/java/com/android/server/TextServicesManagerService.java +++ b/services/core/java/com/android/server/TextServicesManagerService.java @@ -598,7 +598,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { Slog.w(TAG, "bind service: " + info.getId()); } if (!bindCurrentSpellCheckerService(serviceIntent, connection, - Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)) { + Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT_BACKGROUND)) { Slog.e(TAG, "Failed to get a spell checker service."); return null; } diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index cdf25cfe65f6..436ea3cd720a 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -543,7 +543,9 @@ public class GnssLocationProvider implements LocationProviderInterface { loadPropertiesFromResource(context, mProperties); String lpp_profile = mProperties.getProperty("LPP_PROFILE"); // set the persist property LPP_PROFILE for the value - SystemProperties.set(LPP_PROFILE, lpp_profile); + if (lpp_profile != null) { + SystemProperties.set(LPP_PROFILE, lpp_profile); + } } else { // reset the persist property SystemProperties.set(LPP_PROFILE, ""); @@ -1052,8 +1054,15 @@ public class GnssLocationProvider implements LocationProviderInterface { // download tasks overrun. synchronized (mLock) { if (mDownloadXtraWakeLock.isHeld()) { - mDownloadXtraWakeLock.release(); - if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()"); + // This wakelock may have time-out, if a timeout was specified. + // Catch (and ignore) any timeout exceptions. + try { + mDownloadXtraWakeLock.release(); + if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()"); + } catch (Exception e) { + Log.i(TAG, "Wakelock timeout & release race exception in " + + "handleDownloadXtraData()", e); + } } else { Log.e(TAG, "WakeLock expired before release in " + "handleDownloadXtraData()"); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index aead98b90046..b68630e16f8e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2722,14 +2722,17 @@ public class PackageManagerService extends IPackageManager.Stub //delete tmp files deleteTempPackageFiles(); + final int cachedSystemApps = PackageParser.sCachedPackageReadCount.get(); + // Remove any shared userIDs that have no associated packages mSettings.pruneSharedUsersLPw(); final long systemScanTime = SystemClock.uptimeMillis() - startTime; final int systemPackagesCount = mPackages.size(); Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime + " ms, packageCount: " + systemPackagesCount - + " ms, timePerPackage: " - + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)); + + " , timePerPackage: " + + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount) + + " , cached: " + cachedSystemApps); if (mIsUpgrade && systemPackagesCount > 0) { MetricsLogger.histogram(null, "ota_package_manager_system_app_avg_scan_time", ((int) systemScanTime) / systemPackagesCount); @@ -2820,12 +2823,16 @@ public class PackageManagerService extends IPackageManager.Stub // This must be done last to ensure all stubs are replaced or disabled. decompressSystemApplications(stubSystemApps, scanFlags); + final int cachedNonSystemApps = PackageParser.sCachedPackageReadCount.get() + - cachedSystemApps; + final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime; final int dataPackagesCount = mPackages.size() - systemPackagesCount; Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime + " ms, packageCount: " + dataPackagesCount - + " ms, timePerPackage: " - + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)); + + " , timePerPackage: " + + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount) + + " , cached: " + cachedNonSystemApps); if (mIsUpgrade && dataPackagesCount > 0) { MetricsLogger.histogram(null, "ota_package_manager_data_app_avg_scan_time", ((int) dataScanTime) / dataPackagesCount); diff --git a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java index 0508fdff531a..a12c2c40152d 100644 --- a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java +++ b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java @@ -32,7 +32,7 @@ import java.io.DataOutputStream; // the surface control. // // See cts/hostsidetests/../../SurfaceTraceReceiver.java for parsing side. -class RemoteSurfaceTrace extends SurfaceControl { +class RemoteSurfaceTrace extends SurfaceControlWithBackground { static final String TAG = "RemoteSurfaceTrace"; final FileDescriptor mWriteFd; @@ -41,7 +41,8 @@ class RemoteSurfaceTrace extends SurfaceControl { final WindowManagerService mService; final WindowState mWindow; - RemoteSurfaceTrace(FileDescriptor fd, SurfaceControl wrapped, WindowState window) { + RemoteSurfaceTrace(FileDescriptor fd, SurfaceControlWithBackground wrapped, + WindowState window) { super(wrapped); mWriteFd = fd; diff --git a/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java b/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java new file mode 100644 index 000000000000..f5ef2e66f0a6 --- /dev/null +++ b/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java @@ -0,0 +1,306 @@ +/* + * 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 + */ + +package com.android.server.wm; + +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.Region; +import android.os.IBinder; +import android.os.Parcel; +import android.view.Surface; +import android.view.Surface.OutOfResourcesException; +import android.view.SurfaceControl; +import android.view.SurfaceSession; + +/** + * SurfaceControl extension that has background sized to match its container. + */ +class SurfaceControlWithBackground extends SurfaceControl { + // SurfaceControl that holds the background behind opaque letterboxed app windows. + private SurfaceControl mBackgroundControl; + + // Flags that define whether the background should be shown. + private boolean mOpaque; + private boolean mVisible; + + // Way to communicate with corresponding window. + private WindowSurfaceController mWindowSurfaceController; + + // Rect to hold task bounds when computing metrics for background. + private Rect mTmpContainerRect = new Rect(); + + // Last metrics applied to the main SurfaceControl. + private float mLastWidth, mLastHeight; + private float mLastDsDx = 1, mLastDsDy = 1; + private float mLastX, mLastY; + + public SurfaceControlWithBackground(SurfaceControlWithBackground other) { + super(other); + mBackgroundControl = other.mBackgroundControl; + mOpaque = other.mOpaque; + mVisible = other.mVisible; + mWindowSurfaceController = other.mWindowSurfaceController; + } + + public SurfaceControlWithBackground(SurfaceSession s, String name, int w, int h, int format, + int flags, int windowType, int ownerUid, + WindowSurfaceController windowSurfaceController) throws OutOfResourcesException { + super(s, name, w, h, format, flags, windowType, ownerUid); + + // We should only show background when the window is letterboxed in a task. + if (!windowSurfaceController.mAnimator.mWin.isLetterboxedAppWindow()) { + return; + } + mWindowSurfaceController = windowSurfaceController; + mLastWidth = w; + mLastHeight = h; + mOpaque = (flags & SurfaceControl.OPAQUE) != 0; + mWindowSurfaceController.getContainerRect(mTmpContainerRect); + mBackgroundControl = new SurfaceControl(s, "Background for - " + name, + mTmpContainerRect.width(), mTmpContainerRect.height(), PixelFormat.OPAQUE, + flags | SurfaceControl.FX_SURFACE_DIM); + } + + @Override + public void setAlpha(float alpha) { + super.setAlpha(alpha); + + if (mBackgroundControl == null) { + return; + } + mBackgroundControl.setAlpha(alpha); + } + + @Override + public void setLayer(int zorder) { + super.setLayer(zorder); + + if (mBackgroundControl == null) { + return; + } + // TODO: Use setRelativeLayer(Integer.MIN_VALUE) when it's fixed. + mBackgroundControl.setLayer(zorder - 1); + } + + @Override + public void setPosition(float x, float y) { + super.setPosition(x, y); + + if (mBackgroundControl == null) { + return; + } + mLastX = x; + mLastY = y; + updateBgPosition(); + } + + private void updateBgPosition() { + mWindowSurfaceController.getContainerRect(mTmpContainerRect); + final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame; + final float offsetX = (mTmpContainerRect.left - winFrame.left) * mLastDsDx; + final float offsetY = (mTmpContainerRect.top - winFrame.top) * mLastDsDy; + mBackgroundControl.setPosition(mLastX + offsetX, mLastY + offsetY); + } + + @Override + public void setSize(int w, int h) { + super.setSize(w, h); + + if (mBackgroundControl == null) { + return; + } + mLastWidth = w; + mLastHeight = h; + mWindowSurfaceController.getContainerRect(mTmpContainerRect); + mBackgroundControl.setSize(mTmpContainerRect.width(), mTmpContainerRect.height()); + } + + @Override + public void setWindowCrop(Rect crop) { + super.setWindowCrop(crop); + + if (mBackgroundControl == null) { + return; + } + if (crop.width() < mLastWidth || crop.height() < mLastHeight) { + // We're animating and cropping window, compute the appropriate crop for background. + calculateBgCrop(crop); + mBackgroundControl.setWindowCrop(mTmpContainerRect); + } else { + // When not animating just set crop to container rect. + mWindowSurfaceController.getContainerRect(mTmpContainerRect); + mBackgroundControl.setWindowCrop(mTmpContainerRect); + } + } + + @Override + public void setFinalCrop(Rect crop) { + super.setFinalCrop(crop); + + if (mBackgroundControl == null) { + return; + } + if (crop.width() < mLastWidth || crop.height() < mLastHeight) { + // We're animating and cropping window, compute the appropriate crop for background. + calculateBgCrop(crop); + mBackgroundControl.setFinalCrop(mTmpContainerRect); + } else { + // When not animating just set crop to container rect. + mWindowSurfaceController.getContainerRect(mTmpContainerRect); + mBackgroundControl.setFinalCrop(mTmpContainerRect); + } + } + + /** Compute background crop based on current animation progress for main surface control. */ + private void calculateBgCrop(Rect crop) { + // Track overall progress of animation by computing cropped portion of status bar. + final Rect contentInsets = mWindowSurfaceController.mAnimator.mWin.mContentInsets; + float d = contentInsets.top == 0 ? 0 : (float) crop.top / contentInsets.top; + + // Compute additional offset for the background when app window is positioned not at (0,0). + // E.g. landscape with navigation bar on the left. + final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame; + final int offsetX = (int) (winFrame.left * mLastDsDx * d + 0.5); + final int offsetY = (int) (winFrame.top * mLastDsDy * d + 0.5); + + // Compute new scaled width and height for background that will depend on current animation + // progress. Those consist of current crop rect for the main surface + scaled areas outside + // of letterboxed area. + mWindowSurfaceController.getContainerRect(mTmpContainerRect); + final int backgroundWidth = + (int) (crop.width() + (mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5); + final int backgroundHeight = + (int) (crop.height() + (mTmpContainerRect.height() - mLastHeight) * (1 - d) + 0.5); + + mTmpContainerRect.set(crop); + // Make sure that part of background to left/top is visible and scaled. + mTmpContainerRect.offset(offsetX, offsetY); + // Set correct width/height, so that area to right/bottom is cropped properly. + mTmpContainerRect.right = mTmpContainerRect.left + backgroundWidth; + mTmpContainerRect.bottom = mTmpContainerRect.top + backgroundHeight; + } + + @Override + public void setLayerStack(int layerStack) { + super.setLayerStack(layerStack); + + if (mBackgroundControl == null) { + return; + } + mBackgroundControl.setLayerStack(layerStack); + } + + @Override + public void setOpaque(boolean isOpaque) { + super.setOpaque(isOpaque); + mOpaque = isOpaque; + updateBackgroundVisibility(); + } + + @Override + public void setSecure(boolean isSecure) { + super.setSecure(isSecure); + } + + @Override + public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) { + super.setMatrix(dsdx, dtdx, dtdy, dsdy); + + if (mBackgroundControl == null) { + return; + } + mBackgroundControl.setMatrix(dsdx, dtdx, dtdy, dsdy); + mLastDsDx = dsdx; + mLastDsDy = dsdy; + updateBgPosition(); + } + + @Override + public void hide() { + super.hide(); + mVisible = false; + updateBackgroundVisibility(); + } + + @Override + public void show() { + super.show(); + mVisible = true; + updateBackgroundVisibility(); + } + + @Override + public void destroy() { + super.destroy(); + + if (mBackgroundControl == null) { + return; + } + mBackgroundControl.destroy(); + } + + @Override + public void release() { + super.release(); + + if (mBackgroundControl == null) { + return; + } + mBackgroundControl.release(); + } + + @Override + public void setTransparentRegionHint(Region region) { + super.setTransparentRegionHint(region); + + if (mBackgroundControl == null) { + return; + } + mBackgroundControl.setTransparentRegionHint(region); + } + + @Override + public void deferTransactionUntil(IBinder handle, long frame) { + super.deferTransactionUntil(handle, frame); + + if (mBackgroundControl == null) { + return; + } + mBackgroundControl.deferTransactionUntil(handle, frame); + } + + @Override + public void deferTransactionUntil(Surface barrier, long frame) { + super.deferTransactionUntil(barrier, frame); + + if (mBackgroundControl == null) { + return; + } + mBackgroundControl.deferTransactionUntil(barrier, frame); + } + + private void updateBackgroundVisibility() { + if (mBackgroundControl == null) { + return; + } + if (mOpaque && mVisible) { + mBackgroundControl.show(); + } else { + mBackgroundControl.hide(); + } + } +} diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 0c217caf2dba..90a2892a1ac5 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3244,6 +3244,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return !isInMultiWindowMode(); } + /** @return true when the window is in fullscreen task, but has non-fullscreen bounds set. */ + boolean isLetterboxedAppWindow() { + final Task task = getTask(); + final boolean taskIsFullscreen = task != null && task.isFullscreen(); + final boolean appWindowIsFullscreen = mAppToken != null && !mAppToken.hasBounds(); + + return taskIsFullscreen && !appWindowIsFullscreen; + } + /** Returns the appropriate bounds to use for computing frames. */ private void getContainerBounds(Rect outBounds) { if (isInMultiWindowMode()) { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 0cc505ea9d05..86265c297b1b 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1200,7 +1200,8 @@ class WindowStateAnimator { if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Applying decor to crop win=" + w + " mDecorFrame=" + w.mDecorFrame + " mSystemDecorRect=" + mSystemDecorRect); - final boolean fullscreen = w.fillsDisplay(); + final Task task = w.getTask(); + final boolean fullscreen = w.fillsDisplay() || (task != null && task.isFullscreen()); final boolean isFreeformResizing = w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM; @@ -1526,6 +1527,19 @@ class WindowStateAnimator { } } + /** + * Get rect of the task this window is currently in. If there is no task, rect will be set to + * empty. + */ + void getContainerRect(Rect rect) { + final Task task = mWin.getTask(); + if (task != null) { + task.getDimBounds(rect); + } else { + rect.left = rect.top = rect.right = rect.bottom = 0; + } + } + void prepareSurfaceLocked(final boolean recoveringMemory) { final WindowState w = mWin; if (!hasSurface()) { diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index 1728cfbb6ef0..110d5cb90f45 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -50,7 +50,7 @@ class WindowSurfaceController { final WindowStateAnimator mAnimator; - private SurfaceControl mSurfaceControl; + private SurfaceControlWithBackground mSurfaceControl; // Should only be set from within setShown(). private boolean mSurfaceShown = false; @@ -97,15 +97,10 @@ class WindowSurfaceController { mWindowType = windowType; mWindowSession = win.mSession; - if (DEBUG_SURFACE_TRACE) { - mSurfaceControl = new SurfaceTrace( - s, name, w, h, format, flags, windowType, ownerUid); - } else { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl"); - mSurfaceControl = new SurfaceControl( - s, name, w, h, format, flags, windowType, ownerUid); - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - } + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl"); + mSurfaceControl = new SurfaceControlWithBackground( + s, name, w, h, format, flags, windowType, ownerUid, this); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); if (mService.mRoot.mSurfaceTraceEnabled) { mSurfaceControl = new RemoteSurfaceTrace( @@ -118,7 +113,7 @@ class WindowSurfaceController { } void removeRemoteTrace() { - mSurfaceControl = new SurfaceControl(mSurfaceControl); + mSurfaceControl = new SurfaceControlWithBackground(mSurfaceControl); } @@ -291,30 +286,30 @@ class WindowSurfaceController { mSurfaceControl.setGeometryAppliesWithResize(); } - void setMatrixInTransaction(float dsdx, float dtdx, float dsdy, float dtdy, + void setMatrixInTransaction(float dsdx, float dtdx, float dtdy, float dsdy, boolean recoveringMemory) { final boolean matrixChanged = mLastDsdx != dsdx || mLastDtdx != dtdx || - mLastDsdy != dsdy || mLastDtdy != dtdy; + mLastDtdy != dtdy || mLastDsdy != dsdy; if (!matrixChanged) { return; } mLastDsdx = dsdx; mLastDtdx = dtdx; - mLastDsdy = dsdy; mLastDtdy = dtdy; + mLastDsdy = dsdy; try { if (SHOW_TRANSACTIONS) logSurface( - "MATRIX [" + dsdx + "," + dtdx + "," + dsdy + "," + dtdy + "]", null); + "MATRIX [" + dsdx + "," + dtdx + "," + dtdy + "," + dsdy + "]", null); mSurfaceControl.setMatrix( - dsdx, dtdx, dsdy, dtdy); + dsdx, dtdx, dtdy, dsdy); } catch (RuntimeException e) { // If something goes wrong with the surface (such // as running out of memory), don't take down the // entire system. Slog.e(TAG, "Error setting matrix on surface surface" + title - + " MATRIX [" + dsdx + "," + dtdx + "," + dsdy + "," + dtdy + "]", null); + + " MATRIX [" + dsdx + "," + dtdx + "," + dtdy + "," + dsdy + "]", null); if (!recoveringMemory) { mAnimator.reclaimSomeSurfaceMemory("matrix", true); } @@ -421,6 +416,10 @@ class WindowSurfaceController { } } + void getContainerRect(Rect rect) { + mAnimator.getContainerRect(rect); + } + boolean showRobustlyInTransaction() { if (SHOW_TRANSACTIONS) logSurface( "SHOW (performLayout)", null); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 2e7b19a2d1c8..cee77fb642e9 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1654,8 +1654,7 @@ public class TelephonyManager { * @hide */ public String getNetworkCountryIso(int subId) { - int phoneId = SubscriptionManager.getPhoneId(subId); - return getNetworkCountryIsoForPhone(phoneId); + return getNetworkCountryIsoForPhone(getPhoneId(subId)); } /** @@ -1670,7 +1669,14 @@ public class TelephonyManager { */ /** {@hide} */ public String getNetworkCountryIsoForPhone(int phoneId) { - return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, ""); + try { + ITelephony telephony = getITelephony(); + if (telephony == null) + return ""; + return telephony.getNetworkCountryIsoForPhone(phoneId); + } catch (RemoteException ex) { + return ""; + } } /** Network type is unknown */ diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index a0e5b7b0a1d6..654adb280310 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -377,6 +377,13 @@ interface ITelephony { Bundle getCellLocation(String callingPkg); /** + * Returns the ISO country code equivalent of the current registered + * operator's MCC (Mobile Country Code). + * @see android.telephony.TelephonyManager#getNetworkCountryIso + */ + String getNetworkCountryIsoForPhone(int phoneId); + + /** * Returns the neighboring cell information of the device. */ List<NeighboringCellInfo> getNeighboringCellInfo(String callingPkg); diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 9a37913f0edc..a5783a532e23 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -268,8 +268,7 @@ bool ResourceParser::Parse(xml::XmlPullParser* parser) { continue; } - if (!parser->element_namespace().empty() || - parser->element_name() != "resources") { + if (!parser->element_namespace().empty() || parser->element_name() != "resources") { diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) << "root element must be <resources>"); return false; @@ -328,8 +327,7 @@ bool ResourceParser::ParseResources(xml::XmlPullParser* parser) { parsed_resource.comment = std::move(comment); // Extract the product name if it exists. - if (Maybe<StringPiece> maybe_product = - xml::FindNonEmptyAttribute(parser, "product")) { + if (Maybe<StringPiece> maybe_product = xml::FindNonEmptyAttribute(parser, "product")) { parsed_resource.product = maybe_product.value().to_string(); } @@ -348,10 +346,8 @@ bool ResourceParser::ParseResources(xml::XmlPullParser* parser) { for (const ResourceName& stripped_resource : stripped_resources) { if (!table_->FindResource(stripped_resource)) { // Failed to find the resource. - diag_->Error(DiagMessage(source_) - << "resource '" << stripped_resource - << "' " - "was filtered out but no product variant remains"); + diag_->Error(DiagMessage(source_) << "resource '" << stripped_resource + << "' was filtered out but no product variant remains"); error = true; } } @@ -589,7 +585,7 @@ std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser, // This can only be a StyledString. std::unique_ptr<StyledString> styled_string = util::make_unique<StyledString>(table_->string_pool.MakeRef( - style_string, StringPool::Context(StringPool::Context::kStylePriority, config_))); + style_string, StringPool::Context(StringPool::Context::kNormalPriority, config_))); styled_string->untranslatable_sections = std::move(untranslatable_sections); return std::move(styled_string); } diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp index d47a529036bc..1683c64a6a5c 100644 --- a/tools/aapt2/ResourceParser_test.cpp +++ b/tools/aapt2/ResourceParser_test.cpp @@ -117,7 +117,7 @@ TEST_F(ResourceParserTest, ParseStyledString) { StyledString* str = test::GetValue<StyledString>(&table_, "string/foo"); ASSERT_THAT(str, NotNull()); - EXPECT_THAT(*str->value->str, Eq("This is my aunt\u2019s fickle string")); + EXPECT_THAT(str->value->value, Eq("This is my aunt\u2019s fickle string")); EXPECT_THAT(str->value->spans, SizeIs(2)); EXPECT_THAT(str->untranslatable_sections, IsEmpty()); @@ -190,7 +190,7 @@ TEST_F(ResourceParserTest, RecordUntranslateableXliffSectionsInStyledString) { StyledString* str = test::GetValue<StyledString>(&table_, "string/foo"); ASSERT_THAT(str, NotNull()); - EXPECT_THAT(*str->value->str, Eq("There are %1$d apples")); + EXPECT_THAT(str->value->value, Eq("There are %1$d apples")); ASSERT_THAT(str->untranslatable_sections, SizeIs(1)); // We expect indices and lengths that span to include the whitespace diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index 6e6a2ba6fc50..f193fe0c6593 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -700,7 +700,7 @@ std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const Config spans++; } return util::make_unique<StyledString>(dst_pool->MakeRef( - style_str, StringPool::Context(StringPool::Context::kStylePriority, config))); + style_str, StringPool::Context(StringPool::Context::kNormalPriority, config))); } else { if (type != ResourceType::kString && util::StartsWith(str, "res/")) { // This must be a FileReference. diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp index 947e091e2d48..eb59175edf3b 100644 --- a/tools/aapt2/ResourceValues.cpp +++ b/tools/aapt2/ResourceValues.cpp @@ -253,10 +253,9 @@ StyledString* StyledString::Clone(StringPool* new_pool) const { } void StyledString::Print(std::ostream* out) const { - *out << "(styled string) \"" << *value->str << "\""; + *out << "(styled string) \"" << value->value << "\""; for (const StringPool::Span& span : value->spans) { - *out << " " << *span.name << ":" << span.first_char << "," - << span.last_char; + *out << " " << *span.name << ":" << span.first_char << "," << span.last_char; } } diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp index 57da5f01dcd1..705b1ab052af 100644 --- a/tools/aapt2/StringPool.cpp +++ b/tools/aapt2/StringPool.cpp @@ -27,7 +27,7 @@ #include "util/BigBuffer.h" #include "util/Util.h" -using android::StringPiece; +using ::android::StringPiece; namespace aapt { @@ -75,9 +75,14 @@ const std::string* StringPool::Ref::operator->() const { return &entry_->value; } -const std::string& StringPool::Ref::operator*() const { return entry_->value; } +const std::string& StringPool::Ref::operator*() const { + return entry_->value; +} -size_t StringPool::Ref::index() const { return entry_->index; } +size_t StringPool::Ref::index() const { + // Account for the styles, which *always* come first. + return entry_->pool_->styles_.size() + entry_->index_; +} const StringPool::Context& StringPool::Ref::GetContext() const { return entry_->context; @@ -104,8 +109,7 @@ StringPool::StyleRef::~StyleRef() { } } -StringPool::StyleRef& StringPool::StyleRef::operator=( - const StringPool::StyleRef& rhs) { +StringPool::StyleRef& StringPool::StyleRef::operator=(const StringPool::StyleRef& rhs) { if (rhs.entry_ != nullptr) { rhs.entry_->ref_++; } @@ -118,7 +122,7 @@ StringPool::StyleRef& StringPool::StyleRef::operator=( } bool StringPool::StyleRef::operator==(const StyleRef& rhs) const { - if (entry_->str != rhs.entry_->str) { + if (entry_->value != rhs.entry_->value) { return false; } @@ -137,7 +141,9 @@ bool StringPool::StyleRef::operator==(const StyleRef& rhs) const { return true; } -bool StringPool::StyleRef::operator!=(const StyleRef& rhs) const { return !operator==(rhs); } +bool StringPool::StyleRef::operator!=(const StyleRef& rhs) const { + return !operator==(rhs); +} const StringPool::StyleEntry* StringPool::StyleRef::operator->() const { return entry_; @@ -147,23 +153,24 @@ const StringPool::StyleEntry& StringPool::StyleRef::operator*() const { return *entry_; } -size_t StringPool::StyleRef::index() const { return entry_->str.index(); } +size_t StringPool::StyleRef::index() const { + return entry_->index_; +} const StringPool::Context& StringPool::StyleRef::GetContext() const { - return entry_->str.GetContext(); + return entry_->context; } StringPool::Ref StringPool::MakeRef(const StringPiece& str) { return MakeRefImpl(str, Context{}, true); } -StringPool::Ref StringPool::MakeRef(const StringPiece& str, - const Context& context) { +StringPool::Ref StringPool::MakeRef(const StringPiece& str, const Context& context) { return MakeRefImpl(str, context, true); } -StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str, - const Context& context, bool unique) { +StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str, const Context& context, + bool unique) { if (unique) { auto iter = indexed_strings_.find(str); if (iter != std::end(indexed_strings_)) { @@ -171,82 +178,87 @@ StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str, } } - Entry* entry = new Entry(); + std::unique_ptr<Entry> entry(new Entry()); entry->value = str.to_string(); entry->context = context; - entry->index = strings_.size(); + entry->index_ = strings_.size(); entry->ref_ = 0; - strings_.emplace_back(entry); - indexed_strings_.insert(std::make_pair(StringPiece(entry->value), entry)); - return Ref(entry); + entry->pool_ = this; + + Entry* borrow = entry.get(); + strings_.emplace_back(std::move(entry)); + indexed_strings_.insert(std::make_pair(StringPiece(borrow->value), borrow)); + return Ref(borrow); } StringPool::StyleRef StringPool::MakeRef(const StyleString& str) { return MakeRef(str, Context{}); } -StringPool::StyleRef StringPool::MakeRef(const StyleString& str, - const Context& context) { - Entry* entry = new Entry(); +StringPool::StyleRef StringPool::MakeRef(const StyleString& str, const Context& context) { + std::unique_ptr<StyleEntry> entry(new StyleEntry()); entry->value = str.str; entry->context = context; - entry->index = strings_.size(); + entry->index_ = styles_.size(); entry->ref_ = 0; - strings_.emplace_back(entry); - indexed_strings_.insert(std::make_pair(StringPiece(entry->value), entry)); - - StyleEntry* style_entry = new StyleEntry(); - style_entry->str = Ref(entry); for (const aapt::Span& span : str.spans) { - style_entry->spans.emplace_back( - Span{MakeRef(span.name), span.first_char, span.last_char}); + entry->spans.emplace_back(Span{MakeRef(span.name), span.first_char, span.last_char}); } - style_entry->ref_ = 0; - styles_.emplace_back(style_entry); - return StyleRef(style_entry); + + StyleEntry* borrow = entry.get(); + styles_.emplace_back(std::move(entry)); + return StyleRef(borrow); } StringPool::StyleRef StringPool::MakeRef(const StyleRef& ref) { - Entry* entry = new Entry(); - entry->value = *ref.entry_->str; - entry->context = ref.entry_->str.entry_->context; - entry->index = strings_.size(); + std::unique_ptr<StyleEntry> entry(new StyleEntry()); + entry->value = ref.entry_->value; + entry->context = ref.entry_->context; + entry->index_ = styles_.size(); entry->ref_ = 0; - strings_.emplace_back(entry); - indexed_strings_.insert(std::make_pair(StringPiece(entry->value), entry)); - - StyleEntry* style_entry = new StyleEntry(); - style_entry->str = Ref(entry); for (const Span& span : ref.entry_->spans) { - style_entry->spans.emplace_back( - Span{MakeRef(*span.name), span.first_char, span.last_char}); + entry->spans.emplace_back(Span{MakeRef(*span.name), span.first_char, span.last_char}); + } + + StyleEntry* borrow = entry.get(); + styles_.emplace_back(std::move(entry)); + return StyleRef(borrow); +} + +void StringPool::ReAssignIndices() { + // Assign the style indices. + const size_t style_len = styles_.size(); + for (size_t index = 0; index < style_len; index++) { + styles_[index]->index_ = index; + } + + // Assign the string indices. + const size_t string_len = strings_.size(); + for (size_t index = 0; index < string_len; index++) { + strings_[index]->index_ = index; } - style_entry->ref_ = 0; - styles_.emplace_back(style_entry); - return StyleRef(style_entry); } void StringPool::Merge(StringPool&& pool) { - indexed_strings_.insert(pool.indexed_strings_.begin(), - pool.indexed_strings_.end()); - pool.indexed_strings_.clear(); - std::move(pool.strings_.begin(), pool.strings_.end(), - std::back_inserter(strings_)); - pool.strings_.clear(); - std::move(pool.styles_.begin(), pool.styles_.end(), - std::back_inserter(styles_)); + // First, change the owning pool for the incoming strings. + for (std::unique_ptr<Entry>& entry : pool.strings_) { + entry->pool_ = this; + } + + // Now move the styles, strings, and indices over. + std::move(pool.styles_.begin(), pool.styles_.end(), std::back_inserter(styles_)); pool.styles_.clear(); + std::move(pool.strings_.begin(), pool.strings_.end(), std::back_inserter(strings_)); + pool.strings_.clear(); + indexed_strings_.insert(pool.indexed_strings_.begin(), pool.indexed_strings_.end()); + pool.indexed_strings_.clear(); - // Assign the indices. - const size_t len = strings_.size(); - for (size_t index = 0; index < len; index++) { - strings_[index]->index = index; - } + ReAssignIndices(); } -void StringPool::HintWillAdd(size_t stringCount, size_t styleCount) { - strings_.reserve(strings_.size() + stringCount); - styles_.reserve(styles_.size() + styleCount); +void StringPool::HintWillAdd(size_t string_count, size_t style_count) { + strings_.reserve(strings_.size() + string_count); + styles_.reserve(styles_.size() + style_count); } void StringPool::Prune() { @@ -262,47 +274,42 @@ void StringPool::Prune() { auto end_iter2 = std::remove_if(strings_.begin(), strings_.end(), - [](const std::unique_ptr<Entry>& entry) -> bool { - return entry->ref_ <= 0; - }); - - auto end_iter3 = - std::remove_if(styles_.begin(), styles_.end(), - [](const std::unique_ptr<StyleEntry>& entry) -> bool { - return entry->ref_ <= 0; - }); - - // Remove the entries at the end or else we'll be accessing - // a deleted string from the StyleEntry. + [](const std::unique_ptr<Entry>& entry) -> bool { return entry->ref_ <= 0; }); + auto end_iter3 = std::remove_if( + styles_.begin(), styles_.end(), + [](const std::unique_ptr<StyleEntry>& entry) -> bool { return entry->ref_ <= 0; }); + + // Remove the entries at the end or else we'll be accessing a deleted string from the StyleEntry. strings_.erase(end_iter2, strings_.end()); styles_.erase(end_iter3, styles_.end()); - // Reassign the indices. - const size_t len = strings_.size(); - for (size_t index = 0; index < len; index++) { - strings_[index]->index = index; - } + ReAssignIndices(); } -void StringPool::Sort( - const std::function<bool(const Entry&, const Entry&)>& cmp) { - std::sort( - strings_.begin(), strings_.end(), - [&cmp](const std::unique_ptr<Entry>& a, - const std::unique_ptr<Entry>& b) -> bool { return cmp(*a, *b); }); - - // Assign the indices. - const size_t len = strings_.size(); - for (size_t index = 0; index < len; index++) { - strings_[index]->index = index; +template <typename E> +static void SortEntries( + std::vector<std::unique_ptr<E>>& entries, + const std::function<int(const StringPool::Context&, const StringPool::Context&)>& cmp) { + using UEntry = std::unique_ptr<E>; + + if (cmp != nullptr) { + std::sort(entries.begin(), entries.end(), [&cmp](const UEntry& a, const UEntry& b) -> bool { + int r = cmp(a->context, b->context); + if (r == 0) { + r = a->value.compare(b->value); + } + return r < 0; + }); + } else { + std::sort(entries.begin(), entries.end(), + [](const UEntry& a, const UEntry& b) -> bool { return a->value < b->value; }); } +} - // Reorder the styles. - std::sort(styles_.begin(), styles_.end(), - [](const std::unique_ptr<StyleEntry>& lhs, - const std::unique_ptr<StyleEntry>& rhs) -> bool { - return lhs->str.index() < rhs->str.index(); - }); +void StringPool::Sort(const std::function<int(const Context&, const Context&)>& cmp) { + SortEntries(styles_, cmp); + SortEntries(strings_, cmp); + ReAssignIndices(); } template <typename T> @@ -327,60 +334,31 @@ static size_t EncodedLengthUnits(size_t length) { return length > kMaxSize ? 2 : 1; } -bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8) { - const size_t start_index = out->size(); - android::ResStringPool_header* header = - out->NextBlock<android::ResStringPool_header>(); - header->header.type = android::RES_STRING_POOL_TYPE; - header->header.headerSize = sizeof(*header); - header->stringCount = pool.size(); +static void EncodeString(const std::string& str, const bool utf8, BigBuffer* out) { if (utf8) { - header->flags |= android::ResStringPool_header::UTF8_FLAG; - } - - uint32_t* indices = - pool.size() != 0 ? out->NextBlock<uint32_t>(pool.size()) : nullptr; - - uint32_t* style_indices = nullptr; - if (!pool.styles_.empty()) { - header->styleCount = pool.styles_.back()->str.index() + 1; - style_indices = out->NextBlock<uint32_t>(header->styleCount); - } - - const size_t before_strings_index = out->size(); - header->stringsStart = before_strings_index - start_index; - - for (const auto& entry : pool) { - *indices = out->size() - before_strings_index; - indices++; + const std::string& encoded = str; + const ssize_t utf16_length = + utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str.data()), str.size()); + CHECK(utf16_length >= 0); - if (utf8) { - const std::string& encoded = entry->value; - const ssize_t utf16_length = utf8_to_utf16_length( - reinterpret_cast<const uint8_t*>(entry->value.data()), - entry->value.size()); - CHECK(utf16_length >= 0); + const size_t total_size = EncodedLengthUnits<char>(utf16_length) + + EncodedLengthUnits<char>(encoded.length()) + encoded.size() + 1; - const size_t total_size = EncodedLengthUnits<char>(utf16_length) + - EncodedLengthUnits<char>(encoded.length()) + - encoded.size() + 1; + char* data = out->NextBlock<char>(total_size); - char* data = out->NextBlock<char>(total_size); - - // First encode the UTF16 string length. - data = EncodeLength(data, utf16_length); + // First encode the UTF16 string length. + data = EncodeLength(data, utf16_length); - // Now encode the size of the real UTF8 string. - data = EncodeLength(data, encoded.length()); - strncpy(data, encoded.data(), encoded.size()); + // Now encode the size of the real UTF8 string. + data = EncodeLength(data, encoded.length()); + strncpy(data, encoded.data(), encoded.size()); } else { - const std::u16string encoded = util::Utf8ToUtf16(entry->value); + const std::u16string encoded = util::Utf8ToUtf16(str); const ssize_t utf16_length = encoded.size(); // Total number of 16-bit words to write. - const size_t total_size = - EncodedLengthUnits<char16_t>(utf16_length) + encoded.size() + 1; + const size_t total_size = EncodedLengthUnits<char16_t>(utf16_length) + encoded.size() + 1; char16_t* data = out->NextBlock<char16_t>(total_size); @@ -395,31 +373,55 @@ bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8) { // The null-terminating character is already here due to the block of data // being set to 0s on allocation. } +} + +bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8) { + const size_t start_index = out->size(); + android::ResStringPool_header* header = out->NextBlock<android::ResStringPool_header>(); + header->header.type = util::HostToDevice16(android::RES_STRING_POOL_TYPE); + header->header.headerSize = util::HostToDevice16(sizeof(*header)); + header->stringCount = util::HostToDevice32(pool.size()); + header->styleCount = util::HostToDevice32(pool.styles_.size()); + if (utf8) { + header->flags |= android::ResStringPool_header::UTF8_FLAG; } - out->Align4(); + uint32_t* indices = pool.size() != 0 ? out->NextBlock<uint32_t>(pool.size()) : nullptr; + uint32_t* style_indices = + pool.styles_.size() != 0 ? out->NextBlock<uint32_t>(pool.styles_.size()) : nullptr; - if (!pool.styles_.empty()) { - const size_t before_styles_index = out->size(); - header->stylesStart = before_styles_index - start_index; + const size_t before_strings_index = out->size(); + header->stringsStart = before_strings_index - start_index; - size_t current_index = 0; - for (const auto& entry : pool.styles_) { - while (entry->str.index() > current_index) { - style_indices[current_index++] = out->size() - before_styles_index; + // Styles always come first. + for (const std::unique_ptr<StyleEntry>& entry : pool.styles_) { + *indices++ = out->size() - before_strings_index; + EncodeString(entry->value, utf8, out); + } - uint32_t* span_offset = out->NextBlock<uint32_t>(); - *span_offset = android::ResStringPool_span::END; - } - style_indices[current_index++] = out->size() - before_styles_index; - - android::ResStringPool_span* span = - out->NextBlock<android::ResStringPool_span>(entry->spans.size()); - for (const auto& s : entry->spans) { - span->name.index = s.name.index(); - span->firstChar = s.first_char; - span->lastChar = s.last_char; - span++; + for (const std::unique_ptr<Entry>& entry : pool.strings_) { + *indices++ = out->size() - before_strings_index; + EncodeString(entry->value, utf8, out); + } + + out->Align4(); + + if (style_indices != nullptr) { + const size_t before_styles_index = out->size(); + header->stylesStart = util::HostToDevice32(before_styles_index - start_index); + + for (const std::unique_ptr<StyleEntry>& entry : pool.styles_) { + *style_indices++ = out->size() - before_styles_index; + + if (!entry->spans.empty()) { + android::ResStringPool_span* span = + out->NextBlock<android::ResStringPool_span>(entry->spans.size()); + for (const Span& s : entry->spans) { + span->name.index = util::HostToDevice32(s.name.index()); + span->firstChar = util::HostToDevice32(s.first_char); + span->lastChar = util::HostToDevice32(s.last_char); + span++; + } } uint32_t* spanEnd = out->NextBlock<uint32_t>(); @@ -436,7 +438,7 @@ bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8) { memset(padding, 0xff, padding_length); out->Align4(); } - header->header.size = out->size() - start_index; + header->header.size = util::HostToDevice32(out->size() - start_index); return true; } diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h index d1232a29b5aa..8350d0d09108 100644 --- a/tools/aapt2/StringPool.h +++ b/tools/aapt2/StringPool.h @@ -42,12 +42,16 @@ struct StyleString { std::vector<Span> spans; }; +// A StringPool for storing the value of String and StyledString resources. +// Styles and Strings are stored separately, since the runtime variant of this +// class -- ResStringPool -- requires that styled strings *always* appear first, since their +// style data is stored as an array indexed by the same indices as the main string pool array. +// Otherwise, the style data array would have to be sparse and take up more space. class StringPool { public: class Context { public: enum : uint32_t { - kStylePriority = 0u, kHighPriority = 1u, kNormalPriority = 0x7fffffffu, kLowPriority = 0xffffffffu, @@ -58,8 +62,8 @@ class StringPool { Context() = default; Context(uint32_t p, const ConfigDescription& c) : priority(p), config(c) {} explicit Context(uint32_t p) : priority(p) {} - explicit Context(const ConfigDescription& c) - : priority(kNormalPriority), config(c) {} + explicit Context(const ConfigDescription& c) : priority(kNormalPriority), config(c) { + } }; class Entry; @@ -116,13 +120,14 @@ class StringPool { public: std::string value; Context context; - size_t index; private: friend class StringPool; friend class Ref; + size_t index_; int ref_; + const StringPool* pool_; }; struct Span { @@ -133,18 +138,18 @@ class StringPool { class StyleEntry { public: - Ref str; + std::string value; + Context context; std::vector<Span> spans; private: friend class StringPool; friend class StyleRef; + size_t index_; int ref_; }; - using const_iterator = std::vector<std::unique_ptr<Entry>>::const_iterator; - static bool FlattenUtf8(BigBuffer* out, const StringPool& pool); static bool FlattenUtf16(BigBuffer* out, const StringPool& pool); @@ -152,92 +157,61 @@ class StringPool { StringPool(StringPool&&) = default; StringPool& operator=(StringPool&&) = default; - /** - * Adds a string to the pool, unless it already exists. Returns - * a reference to the string in the pool. - */ + // Adds a string to the pool, unless it already exists. Returns a reference to the string in the + // pool. Ref MakeRef(const android::StringPiece& str); - /** - * Adds a string to the pool, unless it already exists, with a context - * object that can be used when sorting the string pool. Returns - * a reference to the string in the pool. - */ + // Adds a string to the pool, unless it already exists, with a context object that can be used + // when sorting the string pool. Returns a reference to the string in the pool. Ref MakeRef(const android::StringPiece& str, const Context& context); - /** - * Adds a style to the string pool and returns a reference to it. - */ + // Adds a style to the string pool and returns a reference to it. StyleRef MakeRef(const StyleString& str); - /** - * Adds a style to the string pool with a context object that - * can be used when sorting the string pool. Returns a reference - * to the style in the string pool. - */ + // Adds a style to the string pool with a context object that can be used when sorting the string + // pool. Returns a reference to the style in the string pool. StyleRef MakeRef(const StyleString& str, const Context& context); - /** - * Adds a style from another string pool. Returns a reference to the - * style in the string pool. - */ + // Adds a style from another string pool. Returns a reference to the style in the string pool. StyleRef MakeRef(const StyleRef& ref); - /** - * Moves pool into this one without coalescing strings. When this - * function returns, pool will be empty. - */ + // Moves pool into this one without coalescing strings. When this function returns, pool will be + // empty. void Merge(StringPool&& pool); - /** - * Returns the number of strings in the table. - */ - inline size_t size() const; + inline const std::vector<std::unique_ptr<Entry>>& strings() const { + return strings_; + } - /** - * Reserves space for strings and styles as an optimization. - */ + // Returns the number of strings in the table. + inline size_t size() const { + return styles_.size() + strings_.size(); + } + + // Reserves space for strings and styles as an optimization. void HintWillAdd(size_t string_count, size_t style_count); - /** - * Sorts the strings according to some comparison function. - */ - void Sort(const std::function<bool(const Entry&, const Entry&)>& cmp); + // Sorts the strings according to their Context using some comparison function. + // Equal Contexts are further sorted by string value, lexicographically. + // If no comparison function is provided, values are only sorted lexicographically. + void Sort(const std::function<int(const Context&, const Context&)>& cmp = nullptr); - /** - * Removes any strings that have no references. - */ + // Removes any strings that have no references. void Prune(); private: DISALLOW_COPY_AND_ASSIGN(StringPool); - friend const_iterator begin(const StringPool& pool); - friend const_iterator end(const StringPool& pool); - static bool Flatten(BigBuffer* out, const StringPool& pool, bool utf8); Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique); + void ReAssignIndices(); std::vector<std::unique_ptr<Entry>> strings_; std::vector<std::unique_ptr<StyleEntry>> styles_; std::unordered_multimap<android::StringPiece, Entry*> indexed_strings_; }; -// -// Inline implementation -// - -inline size_t StringPool::size() const { return strings_.size(); } - -inline StringPool::const_iterator begin(const StringPool& pool) { - return pool.strings_.begin(); -} - -inline StringPool::const_iterator end(const StringPool& pool) { - return pool.strings_.end(); -} - } // namespace aapt #endif // AAPT_STRING_POOL_H diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp index f64a8cf20928..b1e5ce2e28a8 100644 --- a/tools/aapt2/StringPool_test.cpp +++ b/tools/aapt2/StringPool_test.cpp @@ -23,8 +23,12 @@ #include "test/Test.h" #include "util/Util.h" -using android::StringPiece; -using android::StringPiece16; +using ::android::StringPiece; +using ::android::StringPiece16; +using ::testing::Eq; +using ::testing::Ne; +using ::testing::NotNull; +using ::testing::Pointee; namespace aapt { @@ -32,129 +36,127 @@ TEST(StringPoolTest, InsertOneString) { StringPool pool; StringPool::Ref ref = pool.MakeRef("wut"); - EXPECT_EQ(*ref, "wut"); + EXPECT_THAT(*ref, Eq("wut")); } TEST(StringPoolTest, InsertTwoUniqueStrings) { StringPool pool; - StringPool::Ref ref = pool.MakeRef("wut"); - StringPool::Ref ref2 = pool.MakeRef("hey"); + StringPool::Ref ref_a = pool.MakeRef("wut"); + StringPool::Ref ref_b = pool.MakeRef("hey"); - EXPECT_EQ(*ref, "wut"); - EXPECT_EQ(*ref2, "hey"); + EXPECT_THAT(*ref_a, Eq("wut")); + EXPECT_THAT(*ref_b, Eq("hey")); } TEST(StringPoolTest, DoNotInsertNewDuplicateString) { StringPool pool; - StringPool::Ref ref = pool.MakeRef("wut"); - StringPool::Ref ref2 = pool.MakeRef("wut"); + StringPool::Ref ref_a = pool.MakeRef("wut"); + StringPool::Ref ref_b = pool.MakeRef("wut"); - EXPECT_EQ(*ref, "wut"); - EXPECT_EQ(*ref2, "wut"); - EXPECT_EQ(1u, pool.size()); + EXPECT_THAT(*ref_a, Eq("wut")); + EXPECT_THAT(*ref_b, Eq("wut")); + EXPECT_THAT(pool.size(), Eq(1u)); } TEST(StringPoolTest, MaintainInsertionOrderIndex) { StringPool pool; - StringPool::Ref ref = pool.MakeRef("z"); - StringPool::Ref ref2 = pool.MakeRef("a"); - StringPool::Ref ref3 = pool.MakeRef("m"); + StringPool::Ref ref_a = pool.MakeRef("z"); + StringPool::Ref ref_b = pool.MakeRef("a"); + StringPool::Ref ref_c = pool.MakeRef("m"); - EXPECT_EQ(0u, ref.index()); - EXPECT_EQ(1u, ref2.index()); - EXPECT_EQ(2u, ref3.index()); + EXPECT_THAT(ref_a.index(), Eq(0u)); + EXPECT_THAT(ref_b.index(), Eq(1u)); + EXPECT_THAT(ref_c.index(), Eq(2u)); } TEST(StringPoolTest, PruneStringsWithNoReferences) { StringPool pool; - StringPool::Ref refA = pool.MakeRef("foo"); + StringPool::Ref ref_a = pool.MakeRef("foo"); + + { + StringPool::Ref ref_b = pool.MakeRef("wut"); + EXPECT_THAT(*ref_b, Eq("wut")); + EXPECT_THAT(pool.size(), Eq(2u)); + pool.Prune(); + EXPECT_THAT(pool.size(), Eq(2u)); + } + EXPECT_THAT(pool.size(), Eq(2u)); + { - StringPool::Ref ref = pool.MakeRef("wut"); - EXPECT_EQ(*ref, "wut"); - EXPECT_EQ(2u, pool.size()); + StringPool::Ref ref_c = pool.MakeRef("bar"); + EXPECT_THAT(pool.size(), Eq(3u)); + + pool.Prune(); + EXPECT_THAT(pool.size(), Eq(2u)); } - StringPool::Ref refB = pool.MakeRef("bar"); + EXPECT_THAT(pool.size(), Eq(2u)); - EXPECT_EQ(3u, pool.size()); pool.Prune(); - EXPECT_EQ(2u, pool.size()); - StringPool::const_iterator iter = begin(pool); - EXPECT_EQ((*iter)->value, "foo"); - EXPECT_LT((*iter)->index, 2u); - ++iter; - EXPECT_EQ((*iter)->value, "bar"); - EXPECT_LT((*iter)->index, 2u); + EXPECT_THAT(pool.size(), Eq(1u)); } -TEST(StringPoolTest, SortAndMaintainIndexesInReferences) { +TEST(StringPoolTest, SortAndMaintainIndexesInStringReferences) { StringPool pool; - StringPool::Ref ref = pool.MakeRef("z"); - StringPool::StyleRef ref2 = pool.MakeRef(StyleString{{"a"}}); - StringPool::Ref ref3 = pool.MakeRef("m"); + StringPool::Ref ref_a = pool.MakeRef("z"); + StringPool::Ref ref_b = pool.MakeRef("a"); + StringPool::Ref ref_c = pool.MakeRef("m"); - EXPECT_EQ(*ref, "z"); - EXPECT_EQ(0u, ref.index()); + EXPECT_THAT(*ref_a, Eq("z")); + EXPECT_THAT(ref_a.index(), Eq(0u)); - EXPECT_EQ(*(ref2->str), "a"); - EXPECT_EQ(1u, ref2.index()); + EXPECT_THAT(*ref_b, Eq("a")); + EXPECT_THAT(ref_b.index(), Eq(1u)); - EXPECT_EQ(*ref3, "m"); - EXPECT_EQ(2u, ref3.index()); + EXPECT_THAT(*ref_c, Eq("m")); + EXPECT_THAT(ref_c.index(), Eq(2u)); - pool.Sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool { - return a.value < b.value; - }); + pool.Sort(); - EXPECT_EQ(*ref, "z"); - EXPECT_EQ(2u, ref.index()); + EXPECT_THAT(*ref_a, Eq("z")); + EXPECT_THAT(ref_a.index(), Eq(2u)); - EXPECT_EQ(*(ref2->str), "a"); - EXPECT_EQ(0u, ref2.index()); + EXPECT_THAT(*ref_b, Eq("a")); + EXPECT_THAT(ref_b.index(), Eq(0u)); - EXPECT_EQ(*ref3, "m"); - EXPECT_EQ(1u, ref3.index()); + EXPECT_THAT(*ref_c, Eq("m")); + EXPECT_THAT(ref_c.index(), Eq(1u)); } TEST(StringPoolTest, SortAndStillDedupe) { StringPool pool; - StringPool::Ref ref = pool.MakeRef("z"); - StringPool::Ref ref2 = pool.MakeRef("a"); - StringPool::Ref ref3 = pool.MakeRef("m"); + StringPool::Ref ref_a = pool.MakeRef("z"); + StringPool::Ref ref_b = pool.MakeRef("a"); + StringPool::Ref ref_c = pool.MakeRef("m"); - pool.Sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool { - return a.value < b.value; - }); + pool.Sort(); - StringPool::Ref ref4 = pool.MakeRef("z"); - StringPool::Ref ref5 = pool.MakeRef("a"); - StringPool::Ref ref6 = pool.MakeRef("m"); + StringPool::Ref ref_d = pool.MakeRef("z"); + StringPool::Ref ref_e = pool.MakeRef("a"); + StringPool::Ref ref_f = pool.MakeRef("m"); - EXPECT_EQ(ref4.index(), ref.index()); - EXPECT_EQ(ref5.index(), ref2.index()); - EXPECT_EQ(ref6.index(), ref3.index()); + EXPECT_THAT(ref_d.index(), Eq(ref_a.index())); + EXPECT_THAT(ref_e.index(), Eq(ref_b.index())); + EXPECT_THAT(ref_f.index(), Eq(ref_c.index())); } TEST(StringPoolTest, AddStyles) { StringPool pool; - StyleString str{{"android"}, {Span{{"b"}, 2, 6}}}; - - StringPool::StyleRef ref = pool.MakeRef(str); - - EXPECT_EQ(0u, ref.index()); - EXPECT_EQ(std::string("android"), *(ref->str)); - ASSERT_EQ(1u, ref->spans.size()); + StringPool::StyleRef ref = pool.MakeRef(StyleString{{"android"}, {Span{{"b"}, 2, 6}}}); + EXPECT_THAT(ref.index(), Eq(0u)); + EXPECT_THAT(ref->value, Eq("android")); + ASSERT_THAT(ref->spans.size(), Eq(1u)); const StringPool::Span& span = ref->spans.front(); - EXPECT_EQ(*(span.name), "b"); - EXPECT_EQ(2u, span.first_char); - EXPECT_EQ(6u, span.last_char); + EXPECT_THAT(*span.name, Eq("b")); + EXPECT_THAT(span.first_char, Eq(2u)); + EXPECT_THAT(span.last_char, Eq(6u)); } TEST(StringPoolTest, DoNotDedupeStyleWithSameStringAsNonStyle) { @@ -163,9 +165,25 @@ TEST(StringPoolTest, DoNotDedupeStyleWithSameStringAsNonStyle) { StringPool::Ref ref = pool.MakeRef("android"); StyleString str{{"android"}}; - StringPool::StyleRef styleRef = pool.MakeRef(str); + StringPool::StyleRef style_ref = pool.MakeRef(StyleString{{"android"}}); + + EXPECT_THAT(ref.index(), Ne(style_ref.index())); +} + +TEST(StringPoolTest, StylesAndStringsAreSeparateAfterSorting) { + StringPool pool; + + StringPool::StyleRef ref_a = pool.MakeRef(StyleString{{"beta"}}); + StringPool::Ref ref_b = pool.MakeRef("alpha"); + StringPool::StyleRef ref_c = pool.MakeRef(StyleString{{"alpha"}}); + + EXPECT_THAT(ref_b.index(), Ne(ref_c.index())); - EXPECT_NE(ref.index(), styleRef.index()); + pool.Sort(); + + EXPECT_THAT(ref_c.index(), Eq(0u)); + EXPECT_THAT(ref_a.index(), Eq(1u)); + EXPECT_THAT(ref_b.index(), Eq(2u)); } TEST(StringPoolTest, FlattenEmptyStringPoolUtf8) { @@ -177,7 +195,7 @@ TEST(StringPoolTest, FlattenEmptyStringPoolUtf8) { std::unique_ptr<uint8_t[]> data = util::Copy(buffer); ResStringPool test; - ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR); + ASSERT_THAT(test.setTo(data.get(), buffer.size()), Eq(NO_ERROR)); } TEST(StringPoolTest, FlattenOddCharactersUtf16) { @@ -193,9 +211,9 @@ TEST(StringPoolTest, FlattenOddCharactersUtf16) { ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR); size_t len = 0; const char16_t* str = test.stringAt(0, &len); - EXPECT_EQ(1u, len); - EXPECT_EQ(u'\u093f', *str); - EXPECT_EQ(0u, str[1]); + EXPECT_THAT(len, Eq(1u)); + EXPECT_THAT(str, Pointee(Eq(u'\u093f'))); + EXPECT_THAT(str[1], Eq(0u)); } constexpr const char* sLongString = @@ -210,18 +228,20 @@ TEST(StringPoolTest, Flatten) { StringPool pool; - StringPool::Ref ref1 = pool.MakeRef("hello"); - StringPool::Ref ref2 = pool.MakeRef("goodbye"); - StringPool::Ref ref3 = pool.MakeRef(sLongString); - StringPool::Ref ref4 = pool.MakeRef(""); - StringPool::StyleRef ref5 = pool.MakeRef( - StyleString{{"style"}, {Span{{"b"}, 0, 1}, Span{{"i"}, 2, 3}}}); + StringPool::Ref ref_a = pool.MakeRef("hello"); + StringPool::Ref ref_b = pool.MakeRef("goodbye"); + StringPool::Ref ref_c = pool.MakeRef(sLongString); + StringPool::Ref ref_d = pool.MakeRef(""); + StringPool::StyleRef ref_e = + pool.MakeRef(StyleString{{"style"}, {Span{{"b"}, 0, 1}, Span{{"i"}, 2, 3}}}); + + // Styles are always first. + EXPECT_THAT(ref_e.index(), Eq(0u)); - EXPECT_EQ(0u, ref1.index()); - EXPECT_EQ(1u, ref2.index()); - EXPECT_EQ(2u, ref3.index()); - EXPECT_EQ(3u, ref4.index()); - EXPECT_EQ(4u, ref5.index()); + EXPECT_THAT(ref_a.index(), Eq(1u)); + EXPECT_THAT(ref_b.index(), Eq(2u)); + EXPECT_THAT(ref_c.index(), Eq(3u)); + EXPECT_THAT(ref_d.index(), Eq(4u)); BigBuffer buffers[2] = {BigBuffer(1024), BigBuffer(1024)}; StringPool::FlattenUtf8(&buffers[0], pool); @@ -234,38 +254,37 @@ TEST(StringPoolTest, Flatten) { ResStringPool test; ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR); - EXPECT_EQ(std::string("hello"), util::GetString(test, 0)); - EXPECT_EQ(StringPiece16(u"hello"), util::GetString16(test, 0)); + EXPECT_THAT(util::GetString(test, 1), Eq("hello")); + EXPECT_THAT(util::GetString16(test, 1), Eq(u"hello")); - EXPECT_EQ(std::string("goodbye"), util::GetString(test, 1)); - EXPECT_EQ(StringPiece16(u"goodbye"), util::GetString16(test, 1)); + EXPECT_THAT(util::GetString(test, 2), Eq("goodbye")); + EXPECT_THAT(util::GetString16(test, 2), Eq(u"goodbye")); - EXPECT_EQ(StringPiece(sLongString), util::GetString(test, 2)); - EXPECT_EQ(util::Utf8ToUtf16(sLongString), util::GetString16(test, 2).to_string()); + EXPECT_THAT(util::GetString(test, 3), Eq(sLongString)); + EXPECT_THAT(util::GetString16(test, 3), Eq(util::Utf8ToUtf16(sLongString))); size_t len; - EXPECT_TRUE(test.stringAt(3, &len) != nullptr || - test.string8At(3, &len) != nullptr); - - EXPECT_EQ(std::string("style"), util::GetString(test, 4)); - EXPECT_EQ(StringPiece16(u"style"), util::GetString16(test, 4)); - - const ResStringPool_span* span = test.styleAt(4); - ASSERT_NE(nullptr, span); - EXPECT_EQ(std::string("b"), util::GetString(test, span->name.index)); - EXPECT_EQ(StringPiece16(u"b"), util::GetString16(test, span->name.index)); - EXPECT_EQ(0u, span->firstChar); - EXPECT_EQ(1u, span->lastChar); + EXPECT_TRUE(test.stringAt(4, &len) != nullptr || test.string8At(4, &len) != nullptr); + + EXPECT_THAT(util::GetString(test, 0), Eq("style")); + EXPECT_THAT(util::GetString16(test, 0), Eq(u"style")); + + const ResStringPool_span* span = test.styleAt(0); + ASSERT_THAT(span, NotNull()); + EXPECT_THAT(util::GetString(test, span->name.index), Eq("b")); + EXPECT_THAT(util::GetString16(test, span->name.index), Eq(u"b")); + EXPECT_THAT(span->firstChar, Eq(0u)); + EXPECT_THAT(span->lastChar, Eq(1u)); span++; - ASSERT_NE(ResStringPool_span::END, span->name.index); - EXPECT_EQ(std::string("i"), util::GetString(test, span->name.index)); - EXPECT_EQ(StringPiece16(u"i"), util::GetString16(test, span->name.index)); - EXPECT_EQ(2u, span->firstChar); - EXPECT_EQ(3u, span->lastChar); + ASSERT_THAT(span->name.index, Ne(ResStringPool_span::END)); + EXPECT_THAT(util::GetString(test, span->name.index), Eq("i")); + EXPECT_THAT(util::GetString16(test, span->name.index), Eq(u"i")); + EXPECT_THAT(span->firstChar, Eq(2u)); + EXPECT_THAT(span->lastChar, Eq(3u)); span++; - EXPECT_EQ(ResStringPool_span::END, span->name.index); + EXPECT_THAT(span->name.index, Eq(ResStringPool_span::END)); } } diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp index 8741b7b678ec..e1c45d68f611 100644 --- a/tools/aapt2/cmd/Util.cpp +++ b/tools/aapt2/cmd/Util.cpp @@ -179,6 +179,13 @@ std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info, xml::Attribute{"", "configForSplit", app_info.split_name.value()}); } + // Splits may contain more configurations than originally desired (fallback densities, etc.). + // This makes programmatic discovery of split targetting difficult. Encode the original + // split constraints intended for this split. + std::stringstream target_config_str; + target_config_str << util::Joiner(constraints.configs, ","); + manifest_el->attributes.push_back(xml::Attribute{"", "targetConfig", target_config_str.str()}); + std::unique_ptr<xml::Element> application_el = util::make_unique<xml::Element>(); application_el->name = "application"; application_el->attributes.push_back( diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp index a031ea4c31ec..871ed4f01e77 100644 --- a/tools/aapt2/compile/PseudolocaleGenerator.cpp +++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp @@ -120,7 +120,7 @@ std::unique_ptr<StyledString> PseudolocalizeStyledString(StyledString* string, // All Span indices are UTF-16 based, according to the resources.arsc format expected by the // runtime. So we will do all our processing in UTF-16, then convert back. - const std::u16string text16 = util::Utf8ToUtf16(*string->value->str); + const std::u16string text16 = util::Utf8ToUtf16(string->value->value); // Convenient wrapper around the text that allows us to work with StringPieces. const StringPiece16 text(text16); diff --git a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp index b08e1dab35a9..711558aa51c1 100644 --- a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp +++ b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp @@ -31,7 +31,7 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) { util::make_unique<StyledString>(pool.MakeRef(original_style)).get(), Pseudolocalizer::Method::kNone, &pool); - EXPECT_EQ(original_style.str, *new_string->value->str); + EXPECT_EQ(original_style.str, new_string->value->value); ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size()); EXPECT_EQ(std::string("i"), *new_string->value->spans[0].name); @@ -52,7 +52,7 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeStyledString) { util::make_unique<StyledString>(pool.MakeRef(original_style)).get(), Pseudolocalizer::Method::kAccent, &pool); - EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), *new_string->value->str); + EXPECT_EQ(std::string("[Ĥéļļö ŵöŕļð¡ one two]"), new_string->value->value); ASSERT_EQ(original_style.spans.size(), new_string->value->spans.size()); EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char); @@ -79,7 +79,7 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeAdjacentNestedTags) { Pseudolocalizer::Method::kAccent, &pool); ASSERT_NE(nullptr, new_string); ASSERT_EQ(2u, new_string->value->spans.size()); - EXPECT_EQ(std::string("[ɓöļð one]"), *new_string->value->str); + EXPECT_EQ(std::string("[ɓöļð one]"), new_string->value->value); EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name); EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char); @@ -101,7 +101,7 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeAdjacentTagsUnsorted) { Pseudolocalizer::Method::kAccent, &pool); ASSERT_NE(nullptr, new_string); ASSERT_EQ(2u, new_string->value->spans.size()); - EXPECT_EQ(std::string("[ɓöļð one]"), *new_string->value->str); + EXPECT_EQ(std::string("[ɓöļð one]"), new_string->value->value); EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name); EXPECT_EQ(std::u16string(u"[").size(), new_string->value->spans[0].first_char); @@ -126,7 +126,7 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizeNestedAndAdjacentTags) { ASSERT_EQ(4u, new_string->value->spans.size()); EXPECT_EQ(std::string( "[Ţĥîš šéñţéñçé îš ñöţ ŵĥåţ ýöû ţĥîñķ îţ îš åţ åļļ. one two three four five six]"), - *new_string->value->str); + new_string->value->value); EXPECT_EQ(std::string("b"), *new_string->value->spans[0].name); EXPECT_EQ(std::u16string(u"[Ţĥîš šéñţéñçé îš").size(), new_string->value->spans[0].first_char); @@ -165,7 +165,7 @@ TEST(PseudolocaleGeneratorTest, PseudolocalizePartsOfString) { ASSERT_NE(nullptr, new_string); ASSERT_EQ(2u, new_string->value->spans.size()); EXPECT_EQ(std::string("[Ţĥîš šĥöûļð NOT ɓé þšéûðöļöçåļîžéð. one two three four]"), - *new_string->value->str); + new_string->value->value); EXPECT_EQ(std::string("em"), *new_string->value->spans[0].name); EXPECT_EQ(std::u16string(u"[Ţĥîš").size(), new_string->value->spans[0].first_char); @@ -265,7 +265,7 @@ TEST(PseudolocaleGeneratorTest, RespectUntranslateableSections) { ASSERT_NE(nullptr, new_styled_string); // "world" should be untranslated. - EXPECT_NE(std::string::npos, new_styled_string->value->str->find("world")); + EXPECT_NE(std::string::npos, new_styled_string->value->value.find("world")); String* new_string = test::GetValueForConfig<String>(table.get(), "android:string/bar", test::ParseConfigOrDie("en-rXA")); diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp index f4d02262f25c..e5993a65366d 100644 --- a/tools/aapt2/flatten/TableFlattener.cpp +++ b/tools/aapt2/flatten/TableFlattener.cpp @@ -557,19 +557,15 @@ class PackageFlattener { } // namespace bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) { - // We must do this before writing the resources, since the string pool IDs may - // change. - table->string_pool.Sort( - [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool { - int diff = a.context.priority - b.context.priority; - if (diff < 0) return true; - if (diff > 0) return false; - diff = a.context.config.compare(b.context.config); - if (diff < 0) return true; - if (diff > 0) return false; - return a.value < b.value; - }); + // We must do this before writing the resources, since the string pool IDs may change. table->string_pool.Prune(); + table->string_pool.Sort([](const StringPool::Context& a, const StringPool::Context& b) -> int { + int diff = util::compare(a.priority, b.priority); + if (diff == 0) { + diff = a.config.compare(b.config); + } + return diff; + }); // Write the ResTable header. ChunkWriter table_writer(buffer_); diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp index bfebedef2a1e..331ef784a7da 100644 --- a/tools/aapt2/flatten/XmlFlattener.cpp +++ b/tools/aapt2/flatten/XmlFlattener.cpp @@ -180,8 +180,7 @@ class XmlFlattenerVisitor : public xml::Visitor { flatNode->lineNumber = util::HostToDevice32(node->line_number); flatNode->comment.index = util::HostToDevice32(-1); - ResXMLTree_namespaceExt* flat_ns = - writer.NextBlock<ResXMLTree_namespaceExt>(); + ResXMLTree_namespaceExt* flat_ns = writer.NextBlock<ResXMLTree_namespaceExt>(); AddString(node->namespace_prefix, kLowPriority, &flat_ns->prefix); AddString(node->namespace_uri, kLowPriority, &flat_ns->uri); @@ -289,8 +288,7 @@ class XmlFlattenerVisitor : public xml::Visitor { BigBuffer* buffer_; XmlFlattenerOptions options_; - // Scratch vector to filter attributes. We avoid allocations - // making this a member. + // Scratch vector to filter attributes. We avoid allocations making this a member. std::vector<xml::Attribute*> filtered_attrs_; }; @@ -307,10 +305,9 @@ bool XmlFlattener::Flatten(IAaptContext* context, xml::Node* node) { } // Sort the string pool so that attribute resource IDs show up first. - visitor.pool.Sort( - [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool { - return a.context.priority < b.context.priority; - }); + visitor.pool.Sort([](const StringPool::Context& a, const StringPool::Context& b) -> int { + return util::compare(a.priority, b.priority); + }); // Now we flatten the string pool references into the correct places. for (const auto& ref_entry : visitor.string_refs) { @@ -328,15 +325,13 @@ bool XmlFlattener::Flatten(IAaptContext* context, xml::Node* node) { // Write the array of resource IDs, indexed by StringPool order. ChunkWriter res_id_map_writer(buffer_); res_id_map_writer.StartChunk<ResChunk_header>(RES_XML_RESOURCE_MAP_TYPE); - for (const auto& str : visitor.pool) { - ResourceId id = {str->context.priority}; - if (id.id == kLowPriority || !id.is_valid()) { - // When we see the first non-resource ID, - // we're done. + for (const auto& str : visitor.pool.strings()) { + ResourceId id(str->context.priority); + if (str->context.priority == kLowPriority || !id.is_valid()) { + // When we see the first non-resource ID, we're done. break; } - - *res_id_map_writer.NextBlock<uint32_t>() = id.id; + *res_id_map_writer.NextBlock<uint32_t>() = util::HostToDevice32(id.id); } res_id_map_writer.Finish(); } diff --git a/tools/aapt2/proto/ProtoHelpers.cpp b/tools/aapt2/proto/ProtoHelpers.cpp index 38bf4e3bd8eb..6b21364b5eb2 100644 --- a/tools/aapt2/proto/ProtoHelpers.cpp +++ b/tools/aapt2/proto/ProtoHelpers.cpp @@ -18,8 +18,7 @@ namespace aapt { -void SerializeStringPoolToPb(const StringPool& pool, - pb::StringPool* out_pb_pool) { +void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool) { BigBuffer buffer(1024); StringPool::FlattenUtf8(&buffer, pool); @@ -28,14 +27,12 @@ void SerializeStringPoolToPb(const StringPool& pool, size_t offset = 0; for (const BigBuffer::Block& block : buffer) { - data->insert(data->begin() + offset, block.buffer.get(), - block.buffer.get() + block.size); + data->insert(data->begin() + offset, block.buffer.get(), block.buffer.get() + block.size); offset += block.size; } } -void SerializeSourceToPb(const Source& source, StringPool* src_pool, - pb::Source* out_pb_source) { +void SerializeSourceToPb(const Source& source, StringPool* src_pool, pb::Source* out_pb_source) { StringPool::Ref ref = src_pool->MakeRef(source.path); out_pb_source->set_path_idx(static_cast<uint32_t>(ref.index())); if (source.line) { @@ -43,8 +40,7 @@ void SerializeSourceToPb(const Source& source, StringPool* src_pool, } } -void DeserializeSourceFromPb(const pb::Source& pb_source, - const android::ResStringPool& src_pool, +void DeserializeSourceFromPb(const pb::Source& pb_source, const android::ResStringPool& src_pool, Source* out_source) { if (pb_source.has_path_idx()) { out_source->path = util::GetString(src_pool, pb_source.path_idx()); @@ -80,8 +76,7 @@ SymbolState DeserializeVisibilityFromPb( return SymbolState::kUndefined; } -void SerializeConfig(const ConfigDescription& config, - pb::ConfigDescription* out_pb_config) { +void SerializeConfig(const ConfigDescription& config, pb::ConfigDescription* out_pb_config) { android::ResTable_config flat_config = config; flat_config.size = sizeof(flat_config); flat_config.swapHtoD(); @@ -99,8 +94,7 @@ bool DeserializeConfigDescriptionFromPb(const pb::ConfigDescription& pb_config, return false; } - config = reinterpret_cast<const android::ResTable_config*>( - pb_config.data().data()); + config = reinterpret_cast<const android::ResTable_config*>(pb_config.data().data()); out_config->copyFromDtoH(*config); return true; } diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp index 4b5619235c06..37d5ed0cf59d 100644 --- a/tools/aapt2/proto/TableProtoDeserializer.cpp +++ b/tools/aapt2/proto/TableProtoDeserializer.cpp @@ -195,8 +195,7 @@ class PackagePbDeserializer { spans++; } return util::make_unique<StyledString>(pool->MakeRef( - style_str, - StringPool::Context(StringPool::Context::kStylePriority, config))); + style_str, StringPool::Context(StringPool::Context::kNormalPriority, config))); } return util::make_unique<String>( pool->MakeRef(str, StringPool::Context(config))); diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp index d87d64e1cb46..730442c62836 100644 --- a/tools/aapt2/proto/TableProtoSerializer.cpp +++ b/tools/aapt2/proto/TableProtoSerializer.cpp @@ -87,7 +87,9 @@ class PbSerializerVisitor : public RawValueVisitor { pb_prim->set_data(val.data); } - void VisitItem(Item* item) override { LOG(FATAL) << "unimplemented item"; } + void VisitItem(Item* item) override { + LOG(FATAL) << "unimplemented item"; + } void Visit(Attribute* attr) override { pb::Attribute* pb_attr = pb_compound_value()->mutable_attr(); @@ -207,19 +209,15 @@ class PbSerializerVisitor : public RawValueVisitor { } // namespace std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) { - // We must do this before writing the resources, since the string pool IDs may - // change. - table->string_pool.Sort( - [](const StringPool::Entry& a, const StringPool::Entry& b) -> bool { - int diff = a.context.priority - b.context.priority; - if (diff < 0) return true; - if (diff > 0) return false; - diff = a.context.config.compare(b.context.config); - if (diff < 0) return true; - if (diff > 0) return false; - return a.value < b.value; - }); + // We must do this before writing the resources, since the string pool IDs may change. table->string_pool.Prune(); + table->string_pool.Sort([](const StringPool::Context& a, const StringPool::Context& b) -> int { + int diff = util::compare(a.priority, b.priority); + if (diff == 0) { + diff = a.config.compare(b.config); + } + return diff; + }); auto pb_table = util::make_unique<pb::ResourceTable>(); SerializeStringPoolToPb(table->string_pool, pb_table->mutable_string_pool()); diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp index f3116701056b..728d1f4207c4 100644 --- a/tools/aapt2/unflatten/BinaryResourceParser.cpp +++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp @@ -38,20 +38,17 @@ namespace aapt { using namespace android; -using android::base::StringPrintf; +using ::android::base::StringPrintf; namespace { -/* - * Visitor that converts a reference's resource ID to a resource name, - * given a mapping from resource ID to resource name. - */ +// Visitor that converts a reference's resource ID to a resource name, given a mapping from +// resource ID to resource name. class ReferenceIdToNameVisitor : public ValueVisitor { public: using ValueVisitor::Visit; - explicit ReferenceIdToNameVisitor( - const std::map<ResourceId, ResourceName>* mapping) + explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceName>* mapping) : mapping_(mapping) { CHECK(mapping_ != nullptr); } @@ -99,7 +96,7 @@ bool BinaryResourceParser::Parse() { if (parser.chunk()->type != android::RES_TABLE_TYPE) { context_->GetDiagnostics()->Error(DiagMessage(source_) << StringPrintf("unknown chunk of type 0x%02x", - (int)parser.chunk()->type)); + static_cast<int>(parser.chunk()->type))); return false; } @@ -115,7 +112,7 @@ bool BinaryResourceParser::Parse() { context_->GetDiagnostics()->Warn( DiagMessage(source_) << StringPrintf( "unexpected chunk of type 0x%02x trailing RES_TABLE_TYPE", - (int)parser.chunk()->type)); + static_cast<int>(parser.chunk()->type))); } } return true; @@ -165,9 +162,8 @@ bool BinaryResourceParser::ParseTable(const ResChunk_header* chunk) { default: context_->GetDiagnostics()->Warn( - DiagMessage(source_) - << "unexpected chunk type " - << (int)util::DeviceToHost16(parser.chunk()->type)); + DiagMessage(source_) << "unexpected chunk type " + << static_cast<int>(util::DeviceToHost16(parser.chunk()->type))); break; } } @@ -245,8 +241,7 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) { return false; } } else { - context_->GetDiagnostics()->Warn(DiagMessage(source_) - << "unexpected string pool"); + context_->GetDiagnostics()->Warn(DiagMessage(source_) << "unexpected string pool"); } break; @@ -270,9 +265,8 @@ bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) { default: context_->GetDiagnostics()->Warn( - DiagMessage(source_) - << "unexpected chunk type " - << (int)util::DeviceToHost16(parser.chunk()->type)); + DiagMessage(source_) << "unexpected chunk type " + << static_cast<int>(util::DeviceToHost16(parser.chunk()->type))); break; } } diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h index b9ada7704a26..ad3989ea430f 100644 --- a/tools/aapt2/util/Util.h +++ b/tools/aapt2/util/Util.h @@ -70,12 +70,6 @@ bool EndsWith(const android::StringPiece& str, const android::StringPiece& suffi android::StringPiece TrimWhitespace(const android::StringPiece& str); /** - * UTF-16 isspace(). It basically checks for lower range characters that are - * whitespace. - */ -inline bool isspace16(char16_t c) { return c < 0x0080 && isspace(c); } - -/** * Returns an iterator to the first character that is not alpha-numeric and that * is not in the allowedChars set. */ @@ -104,6 +98,16 @@ bool IsJavaPackageName(const android::StringPiece& str); Maybe<std::string> GetFullyQualifiedClassName(const android::StringPiece& package, const android::StringPiece& class_name); +template <typename T> +typename std::enable_if<std::is_arithmetic<T>::value, int>::type compare(const T& a, const T& b) { + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } + return 0; +} + /** * Makes a std::unique_ptr<> with the template parameter inferred by the compiler. * This will be present in C++14 and can be removed then. |