Merge changes from topic 'TransferInstallSession-oc-mr1-dev' into oc-mr1-dev
* changes:
Allow to read all params used to create session.
Allow to transfer+seal a install session
diff --git a/api/current.txt b/api/current.txt
index 2097444..3c9cd8e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -37089,6 +37089,13 @@
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();
@@ -37129,13 +37136,6 @@
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 6da5140..6c1983d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -17264,10 +17264,8 @@
field public static final int IDENTIFIER_TYPE_RDS_PI = 2; // 0x2
field public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; // 0xd
field public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; // 0xc
- field public static final int IDENTIFIER_TYPE_VENDOR1_PRIMARY = 14; // 0xe
- field public static final int IDENTIFIER_TYPE_VENDOR2_PRIMARY = 15; // 0xf
- field public static final int IDENTIFIER_TYPE_VENDOR3_PRIMARY = 16; // 0x10
- field public static final int IDENTIFIER_TYPE_VENDOR4_PRIMARY = 17; // 0x11
+ field public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = 1999; // 0x7cf
+ field public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = 1000; // 0x3e8
field public static final int PROGRAM_TYPE_AM = 1; // 0x1
field public static final int PROGRAM_TYPE_AM_HD = 3; // 0x3
field public static final int PROGRAM_TYPE_DAB = 5; // 0x5
@@ -17275,10 +17273,8 @@
field public static final int PROGRAM_TYPE_FM = 2; // 0x2
field public static final int PROGRAM_TYPE_FM_HD = 4; // 0x4
field public static final int PROGRAM_TYPE_SXM = 7; // 0x7
- field public static final int PROGRAM_TYPE_VENDOR1 = 8; // 0x8
- field public static final int PROGRAM_TYPE_VENDOR2 = 9; // 0x9
- field public static final int PROGRAM_TYPE_VENDOR3 = 10; // 0xa
- field public static final int PROGRAM_TYPE_VENDOR4 = 11; // 0xb
+ field public static final int PROGRAM_TYPE_VENDOR_END = 1999; // 0x7cf
+ field public static final int PROGRAM_TYPE_VENDOR_START = 1000; // 0x3e8
}
public static final class ProgramSelector.Identifier implements android.os.Parcelable {
@@ -40287,6 +40283,13 @@
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();
@@ -40327,13 +40330,6 @@
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 7690a55..839964f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -37281,6 +37281,14 @@
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();
@@ -37321,14 +37329,6 @@
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/annotation/TargetApi.java b/core/java/android/annotation/TargetApi.java
index ea17890..975318e 100644
--- a/core/java/android/annotation/TargetApi.java
+++ b/core/java/android/annotation/TargetApi.java
@@ -16,6 +16,7 @@
package android.annotation;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
@@ -25,7 +26,7 @@
/** Indicates that Lint should treat this type as targeting a given API level, no matter what the
project target is. */
-@Target({TYPE, METHOD, CONSTRUCTOR})
+@Target({TYPE, METHOD, CONSTRUCTOR, FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface TargetApi {
/**
diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java
index 371cd12..f99d1a8 100644
--- a/core/java/android/app/DexLoadReporter.java
+++ b/core/java/android/app/DexLoadReporter.java
@@ -19,6 +19,7 @@
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 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 @@
// The dex path is not a secondary dex file. Nothing to do.
return;
}
- File secondaryProfile = getSecondaryProfileFile(dexPath);
+
+ File realDexPath;
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.
+ // 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 {
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 @@
}
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 e8eca8c..4ae1aaf 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -50,6 +50,8 @@
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.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 @@
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 @@
}
@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 @@
}
}
- 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 @@
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 0000000..44def33
--- /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/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index 94c25b3..2211cee 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -17,6 +17,7 @@
package android.hardware.radio;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -73,10 +74,8 @@
/** SiriusXM Satellite Radio. */
public static final int PROGRAM_TYPE_SXM = 7;
/** Vendor-specific, not synced across devices. */
- public static final int PROGRAM_TYPE_VENDOR1 = 8;
- public static final int PROGRAM_TYPE_VENDOR2 = 9;
- public static final int PROGRAM_TYPE_VENDOR3 = 10;
- public static final int PROGRAM_TYPE_VENDOR4 = 11;
+ public static final int PROGRAM_TYPE_VENDOR_START = 1000;
+ public static final int PROGRAM_TYPE_VENDOR_END = 1999;
@IntDef(prefix = { "PROGRAM_TYPE_" }, value = {
PROGRAM_TYPE_AM,
PROGRAM_TYPE_FM,
@@ -85,11 +84,8 @@
PROGRAM_TYPE_DAB,
PROGRAM_TYPE_DRMO,
PROGRAM_TYPE_SXM,
- PROGRAM_TYPE_VENDOR1,
- PROGRAM_TYPE_VENDOR2,
- PROGRAM_TYPE_VENDOR3,
- PROGRAM_TYPE_VENDOR4,
})
+ @IntRange(from = PROGRAM_TYPE_VENDOR_START, to = PROGRAM_TYPE_VENDOR_END)
@Retention(RetentionPolicy.SOURCE)
public @interface ProgramType {}
@@ -145,12 +141,12 @@
* Primary identifier for vendor-specific radio technology.
* The value format is determined by a vendor.
*
- * It must not be used in any other programType than VENDORx.
+ * It must not be used in any other programType than corresponding VENDOR
+ * type between VENDOR_START and VENDOR_END (eg. identifier type 1015 must
+ * not be used in any program type other than 1015).
*/
- public static final int IDENTIFIER_TYPE_VENDOR1_PRIMARY = 14;
- public static final int IDENTIFIER_TYPE_VENDOR2_PRIMARY = 15;
- public static final int IDENTIFIER_TYPE_VENDOR3_PRIMARY = 16;
- public static final int IDENTIFIER_TYPE_VENDOR4_PRIMARY = 17;
+ public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = PROGRAM_TYPE_VENDOR_START;
+ public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = PROGRAM_TYPE_VENDOR_END;
@IntDef(prefix = { "IDENTIFIER_TYPE_" }, value = {
IDENTIFIER_TYPE_AMFM_FREQUENCY,
IDENTIFIER_TYPE_RDS_PI,
@@ -165,11 +161,8 @@
IDENTIFIER_TYPE_DRMO_MODULATION,
IDENTIFIER_TYPE_SXM_SERVICE_ID,
IDENTIFIER_TYPE_SXM_CHANNEL,
- IDENTIFIER_TYPE_VENDOR1_PRIMARY,
- IDENTIFIER_TYPE_VENDOR2_PRIMARY,
- IDENTIFIER_TYPE_VENDOR3_PRIMARY,
- IDENTIFIER_TYPE_VENDOR4_PRIMARY,
})
+ @IntRange(from = IDENTIFIER_TYPE_VENDOR_PRIMARY_START, to = IDENTIFIER_TYPE_VENDOR_PRIMARY_END)
@Retention(RetentionPolicy.SOURCE)
public @interface IdentifierType {}
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index a5763ef..5312dca 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.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 @@
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 @@
*/
/* 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;
- }
-
- 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 "
- + 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);
+ final Parcel source = mParcelledData;
+ if (source != null) {
+ initializeFromParcelLocked(source, /*recycleParcel=*/ true);
} else {
- map.erase();
- map.ensureCapacity(N);
- }
- 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;
+ if (DEBUG) {
+ Log.d(TAG, "unparcel "
+ + Integer.toHexString(System.identityHashCode(this))
+ + ": no parcelled data");
}
- } finally {
- mMap = map;
- parcelledData.recycle();
- mParcelledData = null;
}
- if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ }
+ }
+
+ 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(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;
+ }
+
+ 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();
+ } else {
+ throw e;
+ }
+ } finally {
+ mMap = map;
+ if (recycleParcel) {
+ recycleParcel(parcelledData);
+ }
+ mParcelledData = null;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+ " final map: " + mMap);
}
}
@@ -286,7 +302,20 @@
* @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 @@
* @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 @@
+ 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 cc6b5e1..6d44330 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -206,8 +206,15 @@
* - 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 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 @@
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 51f96ee..c58153a 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -22,6 +22,8 @@
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 @@
* @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 @@
* 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 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 2efb0f5..fae9d53 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -291,7 +291,7 @@
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 @@
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 @@
};
/**
+ * @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 @@
if (DEBUG_RECYCLE) {
p.mStack = new RuntimeException();
}
+ p.mReadWriteHelper = ReadWriteHelper.DEFAULT;
return p;
}
}
@@ -385,6 +413,25 @@
}
}
+ /**
+ * 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 @@
* 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 @@
* 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 @@
if (mOwnsNativeParcelObject) {
updateNativeSize(nativeFreeBuffer(mNativePtr));
}
+ mReadWriteHelper = ReadWriteHelper.DEFAULT;
}
private void destroy() {
@@ -3007,6 +3077,7 @@
}
mNativePtr = 0;
}
+ mReadWriteHelper = null;
}
@Override
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index 0edb154..3da0b5e 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -32,7 +32,7 @@
*
* <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 @@
* // 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
similarity index 79%
rename from core/java/android/service/autofill/SimpleRegexValidator.java
rename to core/java/android/service/autofill/RegexValidator.java
index ef8c52c9..9dfe78d 100644
--- a/core/java/android/service/autofill/SimpleRegexValidator.java
+++ b/core/java/android/service/autofill/RegexValidator.java
@@ -34,9 +34,9 @@
*
* <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 @@
* 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 String toString() {
if (!sDebug) return super.toString();
- return "SimpleRegexValidator: [id=" + mId + ", regex=" + mRegex + "]";
+ return "RegexValidator: [id=" + mId + ", regex=" + mRegex + "]";
}
/////////////////////////////////////
@@ -93,17 +93,17 @@
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 f8a94d6..e0a0730 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -470,7 +470,7 @@
* <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 @@
* 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 @@
* 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 @@
*
* 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 44c88e1..1a2968f 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -65,7 +65,8 @@
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 e59cf84..e2bcea0 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -119,7 +119,7 @@
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 @@
LongSamplingCounter mUserCpuTime;
LongSamplingCounter mSystemCpuTime;
- LongSamplingCounter[][] mCpuClusterSpeed;
+ LongSamplingCounter[][] mCpuClusterSpeedTimesUs;
LongSamplingCounterArray mCpuFreqTimeMs;
LongSamplingCounterArray mScreenOffCpuFreqTimeMs;
@@ -6556,12 +6556,12 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
/**
* 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 @@
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 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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
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 9d52e19..bb743c1 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -22,6 +22,7 @@
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 @@
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 9c7debb..757a112 100644
--- a/core/java/com/android/internal/os/KernelCpuSpeedReader.java
+++ b/core/java/com/android/internal/os/KernelCpuSpeedReader.java
@@ -39,8 +39,8 @@
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 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 @@
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 4b01736..2c430e0 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 0000000..00be822
--- /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 73c153f..fda0f1e 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.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 java.io.File;
import java.io.InputStream;
import java.util.Arrays;
+import java.util.function.Function;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -271,22 +273,27 @@
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 @@
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() throws Exception {
+ public void testPackageWithComponents_no_cache() throws Exception {
+ checkPackageWithComponents(p -> p);
+ }
+
+ @Test
+ 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 @@
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 0000000..9fcf96d
--- /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 8fd4e31..0294095 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 @@
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 6664456..4ea4e38 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.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 @@
* @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 @@
* {@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 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/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
index 1240e05..cc7798e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
@@ -31,4 +31,5 @@
void sendRecentsDrawnEvent();
void sendDockingTopTaskEvent(int dragMode, in Rect initialRect);
void sendLaunchRecentsEvent();
+ void setWaitingForTransitionStartEvent(boolean waitingForTransitionStart);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index a5c994e..d10e080 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -54,9 +54,11 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
import com.android.systemui.recents.events.component.ShowUserToastEvent;
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -612,6 +614,14 @@
}
});
}
+
+ // This will catch the cases when a user launches from recents to another app
+ // (and vice versa) that is not in the recents stack (such as home or bugreport) and it
+ // would not reset the wait for transition flag. This will catch it and make sure that the
+ // flag is reset.
+ if (!event.visible) {
+ mImpl.setWaitingForTransitionStart(false);
+ }
}
/**
@@ -684,6 +694,11 @@
}
}
+ public final void onBusEvent(LaunchTaskFailedEvent event) {
+ // Reset the transition when tasks fail to launch
+ mImpl.setWaitingForTransitionStart(false);
+ }
+
public final void onBusEvent(ConfigurationChangedEvent event) {
// Update the configuration for the Recents component when the activity configuration
// changes as well
@@ -711,6 +726,25 @@
}
}
+ public final void onBusEvent(SetWaitingForTransitionStartEvent event) {
+ int processUser = sSystemServicesProxy.getProcessUser();
+ if (sSystemServicesProxy.isSystemUser(processUser)) {
+ mImpl.setWaitingForTransitionStart(event.waitingForTransitionStart);
+ } else {
+ postToSystemUser(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mUserToSystemCallbacks.setWaitingForTransitionStartEvent(
+ event.waitingForTransitionStart);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Callback failed", e);
+ }
+ }
+ });
+ }
+ }
+
/**
* Attempts to register with the system user.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 7cd93bb..86e93fd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -24,12 +24,11 @@
import android.app.ActivityManager;
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityOptions;
+import android.app.ActivityOptions.OnAnimationStartedListener;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -208,6 +207,20 @@
protected static RecentsTaskLoadPlan sInstanceLoadPlan;
// Stores the last pinned task time
protected static long sLastPipTime = -1;
+ // Stores whether we are waiting for a transition to/from recents to start. During this time,
+ // we disallow the user from manually toggling recents until the transition has started.
+ private static boolean mWaitingForTransitionStart = false;
+ // Stores whether or not the user toggled while we were waiting for a transition to/from
+ // recents. In this case, we defer the toggle state until then and apply it immediately after.
+ private static boolean mToggleFollowingTransitionStart = true;
+
+ private ActivityOptions.OnAnimationStartedListener mResetToggleFlagListener =
+ new OnAnimationStartedListener() {
+ @Override
+ public void onAnimationStarted() {
+ setWaitingForTransitionStart(false);
+ }
+ };
protected Context mContext;
protected Handler mHandler;
@@ -365,6 +378,11 @@
return;
}
+ if (mWaitingForTransitionStart) {
+ mToggleFollowingTransitionStart = true;
+ return;
+ }
+
mDraggingInRecents = false;
mLaunchedWhileDocking = false;
mTriggeredFromAltTab = false;
@@ -638,6 +656,18 @@
}
}
+ public void setWaitingForTransitionStart(boolean waitingForTransitionStart) {
+ if (mWaitingForTransitionStart == waitingForTransitionStart) {
+ return;
+ }
+
+ mWaitingForTransitionStart = waitingForTransitionStart;
+ if (!waitingForTransitionStart && mToggleFollowingTransitionStart) {
+ toggleRecents(DividerView.INVALID_RECENTS_GROW_TARGET);
+ }
+ mToggleFollowingTransitionStart = false;
+ }
+
/**
* Returns the preloaded load plan and invalidates it.
*/
@@ -865,8 +895,9 @@
}
AppTransitionAnimationSpec[] specsArray = new AppTransitionAnimationSpec[specs.size()];
specs.toArray(specsArray);
+
return new Pair<>(ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
- specsArray, mHandler, null, this), null);
+ specsArray, mHandler, mResetToggleFlagListener, this), null);
} else {
// Update the destination rect
Task toTask = new Task();
@@ -884,8 +915,10 @@
return Lists.newArrayList(new AppTransitionAnimationSpec(
toTask.key.id, thumbnail, rect));
});
+
return new Pair<>(ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext,
- mHandler, future.getFuture(), null, false /* scaleUp */), future);
+ mHandler, future.getFuture(), mResetToggleFlagListener, false /* scaleUp */),
+ future);
}
}
@@ -991,6 +1024,10 @@
launchState.launchedToTaskId = runningTaskId;
launchState.launchedWithAltTab = mTriggeredFromAltTab;
+ // Disable toggling of recents between starting the activity and it is visible and the app
+ // has started its transition into recents.
+ setWaitingForTransitionStart(useThumbnailTransition);
+
// Preload the icon (this will be a null-op if we have preloaded the icon already in
// preloadRecents())
preloadIcon(runningTaskId);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
index 3921a20..1285626 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
@@ -29,6 +29,7 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
+import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
import com.android.systemui.recents.misc.ForegroundThread;
@@ -105,4 +106,10 @@
public void sendLaunchRecentsEvent() throws RemoteException {
EventBus.getDefault().post(new RecentsActivityStartingEvent());
}
+
+ @Override
+ public void setWaitingForTransitionStartEvent(boolean waitingForTransitionStart) {
+ EventBus.getDefault().post(new SetWaitingForTransitionStartEvent(
+ waitingForTransitionStart));
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java
new file mode 100644
index 0000000..d9cf5fb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java
@@ -0,0 +1,31 @@
+/*
+ * 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.systemui.recents.events.component;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when we are setting/resetting the flag to wait for the transition to start.
+ */
+public class SetWaitingForTransitionStartEvent extends EventBus.Event {
+
+ public final boolean waitingForTransitionStart;
+
+ public SetWaitingForTransitionStartEvent(boolean waitingForTransitionStart) {
+ this.waitingForTransitionStart = waitingForTransitionStart;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index ce40f20..127822a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -54,6 +54,7 @@
import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
@@ -117,31 +118,58 @@
final Rect windowRect = Recents.getSystemServices().getWindowRect();
transitionFuture = getAppTransitionFuture(
() -> composeAnimationSpecs(task, stackView, destinationStack, windowRect));
- animStartedListener = () -> {
- // If we are launching into another task, cancel the previous task's
- // window transition
- EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
- EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
- stackView.cancelAllTaskViewAnimations();
+ animStartedListener = new OnAnimationStartedListener() {
+ private boolean mHandled;
- if (screenPinningRequested) {
- // Request screen pinning after the animation runs
- mStartScreenPinningRunnable.taskId = task.key.id;
- mHandler.postDelayed(mStartScreenPinningRunnable, 350);
+ @Override
+ public void onAnimationStarted() {
+ if (mHandled) {
+ return;
+ }
+ mHandled = true;
+
+ // If we are launching into another task, cancel the previous task's
+ // window transition
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
+ EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+ stackView.cancelAllTaskViewAnimations();
+
+ if (screenPinningRequested) {
+ // Request screen pinning after the animation runs
+ mStartScreenPinningRunnable.taskId = task.key.id;
+ mHandler.postDelayed(mStartScreenPinningRunnable, 350);
+ }
+
+ // Reset the state where we are waiting for the transition to start
+ EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
}
};
} else {
// This is only the case if the task is not on screen (scrolled offscreen for example)
transitionFuture = null;
- animStartedListener = () -> {
- // If we are launching into another task, cancel the previous task's
- // window transition
- EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
- EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
- stackView.cancelAllTaskViewAnimations();
+ animStartedListener = new OnAnimationStartedListener() {
+ private boolean mHandled;
+
+ @Override
+ public void onAnimationStarted() {
+ if (mHandled) {
+ return;
+ }
+ mHandled = true;
+
+ // If we are launching into another task, cancel the previous task's
+ // window transition
+ EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
+ EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+ stackView.cancelAllTaskViewAnimations();
+
+ // Reset the state where we are waiting for the transition to start
+ EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
+ }
};
}
+ EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(true));
final ActivityOptions opts = ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext,
mHandler, transitionFuture != null ? transitionFuture.future : null,
animStartedListener, true /* scaleUp */);
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 472e30c..188f216 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 @@
* 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 814e4be..c23757f 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -214,8 +214,7 @@
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 @@
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 80d39f5..13e3ae6 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -598,7 +598,7 @@
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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bcad2fc..161bf9a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13323,7 +13323,6 @@
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
final ActivityOptions activityOptions = r.pendingOptions;
- r.pendingOptions = null;
return activityOptions == null ? null : activityOptions.toBundle();
}
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 cdf25cf..436ea3c 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 @@
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 @@
// 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 aead98b..b68630e 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 @@
//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 @@
// 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 0508fdf..a12c2c4 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 @@
// 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 @@
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 0000000..f5ef2e6
--- /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 0c217ca..90a2892 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 @@
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 0cc505e..86265c29 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 @@
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 @@
}
}
+ /**
+ * 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 1728cfb..110d5cb 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 @@
final WindowStateAnimator mAnimator;
- private SurfaceControl mSurfaceControl;
+ private SurfaceControlWithBackground mSurfaceControl;
// Should only be set from within setShown().
private boolean mSurfaceShown = false;
@@ -97,15 +97,10 @@
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 @@
}
void removeRemoteTrace() {
- mSurfaceControl = new SurfaceControl(mSurfaceControl);
+ mSurfaceControl = new SurfaceControlWithBackground(mSurfaceControl);
}
@@ -291,30 +286,30 @@
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 @@
}
}
+ 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 2e7b19a..cee77fb 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1654,8 +1654,7 @@
* @hide
*/
public String getNetworkCountryIso(int subId) {
- int phoneId = SubscriptionManager.getPhoneId(subId);
- return getNetworkCountryIsoForPhone(phoneId);
+ return getNetworkCountryIsoForPhone(getPhoneId(subId));
}
/**
@@ -1670,7 +1669,14 @@
*/
/** {@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 a0e5b7b..654adb2 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -377,6 +377,13 @@
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 9a37913..a5783a5 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -268,8 +268,7 @@
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 @@
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 @@
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 @@
// 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 d47a529..1683c64 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -117,7 +117,7 @@
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 @@
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 6e6a2ba..f193fe0 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -700,7 +700,7 @@
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 947e091..eb59175 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -253,10 +253,9 @@
}
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 57da5f0..705b1ab 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 @@
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& 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 @@
}
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 @@
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 @@
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 @@
}
}
- 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});
}
- 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);
+}
+
+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;
+ }
}
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_));
- pool.styles_.clear();
-
- // Assign the indices.
- const size_t len = strings_.size();
- for (size_t index = 0; index < len; index++) {
- strings_[index]->index = index;
+ // 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();
+
+ 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 @@
auto end_iter2 =
std::remove_if(strings_.begin(), strings_.end(),
- [](const std::unique_ptr<Entry>& entry) -> bool {
- return entry->ref_ <= 0;
- });
+ [](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; });
- 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.
+ // 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();
+}
+
+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; });
}
}
-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;
- }
-
- // 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 @@
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;
- }
+ 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);
- uint32_t* indices =
- pool.size() != 0 ? out->NextBlock<uint32_t>(pool.size()) : nullptr;
+ const size_t total_size = EncodedLengthUnits<char>(utf16_length) +
+ EncodedLengthUnits<char>(encoded.length()) + encoded.size() + 1;
- 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);
- }
+ char* data = out->NextBlock<char>(total_size);
- const size_t before_strings_index = out->size();
- header->stringsStart = before_strings_index - start_index;
+ // First encode the UTF16 string length.
+ data = EncodeLength(data, utf16_length);
- for (const auto& entry : pool) {
- *indices = out->size() - before_strings_index;
- indices++;
-
- 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;
-
- char* data = out->NextBlock<char>(total_size);
-
- // 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 @@
// 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;
+ }
+
+ 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;
+
+ const size_t before_strings_index = out->size();
+ header->stringsStart = before_strings_index - start_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);
+ }
+
+ for (const std::unique_ptr<Entry>& entry : pool.strings_) {
+ *indices++ = out->size() - before_strings_index;
+ EncodeString(entry->value, utf8, out);
}
out->Align4();
- if (!pool.styles_.empty()) {
+ if (style_indices != nullptr) {
const size_t before_styles_index = out->size();
- header->stylesStart = before_styles_index - start_index;
+ header->stylesStart = util::HostToDevice32(before_styles_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;
+ for (const std::unique_ptr<StyleEntry>& entry : pool.styles_) {
+ *style_indices++ = out->size() - before_styles_index;
- 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++;
+ 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 @@
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 d1232a2..8350d0d 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -42,12 +42,16 @@
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 @@
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 @@
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 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 @@
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 f64a8cf..b1e5ce2 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 @@
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 = pool.MakeRef("wut");
- EXPECT_EQ(*ref, "wut");
- EXPECT_EQ(2u, pool.size());
- }
- StringPool::Ref refB = pool.MakeRef("bar");
+ StringPool::Ref ref_a = pool.MakeRef("foo");
- EXPECT_EQ(3u, pool.size());
+ {
+ 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_c = pool.MakeRef("bar");
+ EXPECT_THAT(pool.size(), Eq(3u));
+
+ pool.Prune();
+ EXPECT_THAT(pool.size(), Eq(2u));
+ }
+ EXPECT_THAT(pool.size(), Eq(2u));
+
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 @@
StringPool::Ref ref = pool.MakeRef("android");
StyleString str{{"android"}};
- StringPool::StyleRef styleRef = pool.MakeRef(str);
+ StringPool::StyleRef style_ref = pool.MakeRef(StyleString{{"android"}});
- EXPECT_NE(ref.index(), styleRef.index());
+ 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()));
+
+ 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 @@
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 @@
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 @@
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}}});
- 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());
+ // Styles are always first.
+ EXPECT_THAT(ref_e.index(), Eq(0u));
+
+ 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 @@
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_TRUE(test.stringAt(4, &len) != nullptr || test.string8At(4, &len) != nullptr);
- EXPECT_EQ(std::string("style"), util::GetString(test, 4));
- EXPECT_EQ(StringPiece16(u"style"), util::GetString16(test, 4));
+ EXPECT_THAT(util::GetString(test, 0), Eq("style"));
+ EXPECT_THAT(util::GetString16(test, 0), Eq(u"style"));
- 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);
+ 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 8741b7b..e1c45d6 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -179,6 +179,13 @@
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 a031ea4..871ed4f 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -120,7 +120,7 @@
// 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 b08e1da..711558a 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator_test.cpp
@@ -31,7 +31,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 f4d0226..e5993a6 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -557,19 +557,15 @@
} // 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 bfebedef..331ef78 100644
--- a/tools/aapt2/flatten/XmlFlattener.cpp
+++ b/tools/aapt2/flatten/XmlFlattener.cpp
@@ -180,8 +180,7 @@
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 @@
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 @@
}
// 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 @@
// 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 38bf4e3..6b21364 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 @@
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 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 @@
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 @@
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 4b56192..37d5ed0 100644
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -195,8 +195,7 @@
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 d87d64e..730442c 100644
--- a/tools/aapt2/proto/TableProtoSerializer.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer.cpp
@@ -87,7 +87,9 @@
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 @@
} // 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 f311670..728d1f4 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -38,20 +38,17 @@
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 @@
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 @@
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 @@
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 @@
return false;
}
} else {
- context_->GetDiagnostics()->Warn(DiagMessage(source_)
- << "unexpected string pool");
+ context_->GetDiagnostics()->Warn(DiagMessage(source_) << "unexpected string pool");
}
break;
@@ -270,9 +265,8 @@
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 b9ada77..ad3989e 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -70,12 +70,6 @@
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 @@
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.