summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java4
-rw-r--r--core/java/android/os/HidlSupport.java23
-rw-r--r--core/java/android/os/HwRemoteBinder.java5
-rw-r--r--core/java/android/os/TokenWatcher.java30
-rw-r--r--core/java/com/android/internal/util/StateMachine.java2
-rw-r--r--core/jni/android_os_HwBlob.cpp46
-rw-r--r--core/jni/android_os_HwRemoteBinder.cpp49
-rw-r--r--core/jni/android_util_Binder.cpp1
-rw-r--r--core/res/res/values/config.xml8
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java95
-rw-r--r--libs/androidfw/ZipUtils.cpp235
-rw-r--r--libs/androidfw/include/androidfw/ZipUtils.h2
-rw-r--r--services/core/java/com/android/server/pm/OtaDexoptService.java5
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java81
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java77
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java16
-rw-r--r--services/core/java/com/android/server/pm/dex/DexoptUtils.java62
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java209
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java2
-rw-r--r--telephony/java/android/telephony/Telephony.java65
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java11
21 files changed, 706 insertions, 322 deletions
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 06f7916be4b6..70af021fe0c3 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -688,6 +688,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* Cycles do not exist because they are illegal and screened for during installation.
*
* May be null if no splits are installed, or if no dependencies exist between them.
+ *
+ * NOTE: Any change to the way split dependencies are stored must update the logic that
+ * creates the class loader context for dexopt (DexoptUtils#getClassLoaderContexts).
+ *
* @hide
*/
public SparseArray<int[]> splitDependencies;
diff --git a/core/java/android/os/HidlSupport.java b/core/java/android/os/HidlSupport.java
index 7dec4d724f15..3544ea1e03e3 100644
--- a/core/java/android/os/HidlSupport.java
+++ b/core/java/android/os/HidlSupport.java
@@ -156,4 +156,27 @@ public class HidlSupport {
// Should not reach here.
throw new UnsupportedOperationException();
}
+
+ /**
+ * Test that two interfaces are equal. This is the Java equivalent to C++
+ * interfacesEqual function.
+ * This essentially calls .equals on the internal binder objects (via Binder()).
+ * - If both interfaces are proxies, asBinder() returns a {@link HwRemoteBinder}
+ * object, and they are compared in {@link HwRemoteBinder#equals}.
+ * - If both interfaces are stubs, asBinder() returns the object itself. By default,
+ * auto-generated IFoo.Stub does not override equals(), but an implementation can
+ * optionally override it, and {@code interfacesEqual} will use it here.
+ */
+ public static boolean interfacesEqual(IHwInterface lft, Object rgt) {
+ if (lft == rgt) {
+ return true;
+ }
+ if (lft == null || rgt == null) {
+ return false;
+ }
+ if (!(rgt instanceof IHwInterface)) {
+ return false;
+ }
+ return Objects.equals(lft.asBinder(), ((IHwInterface) rgt).asBinder());
+ }
}
diff --git a/core/java/android/os/HwRemoteBinder.java b/core/java/android/os/HwRemoteBinder.java
index 2f89ce6270be..a07e42c720c5 100644
--- a/core/java/android/os/HwRemoteBinder.java
+++ b/core/java/android/os/HwRemoteBinder.java
@@ -63,4 +63,9 @@ public class HwRemoteBinder implements IHwBinder {
}
private long mNativeContext;
+
+ @Override
+ public final native boolean equals(Object other);
+ @Override
+ public final native int hashCode();
}
diff --git a/core/java/android/os/TokenWatcher.java b/core/java/android/os/TokenWatcher.java
index 9b3a2d689693..00333dad1a17 100644
--- a/core/java/android/os/TokenWatcher.java
+++ b/core/java/android/os/TokenWatcher.java
@@ -16,17 +16,23 @@
package android.os;
+import android.util.Log;
+
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.WeakHashMap;
import java.util.Set;
-import android.util.Log;
+import java.util.WeakHashMap;
/**
- * Helper class that helps you use IBinder objects as reference counted
- * tokens. IBinders make good tokens because we find out when they are
- * removed
+ * A TokenWatcher watches a collection of {@link IBinder}s. IBinders are added
+ * to the collection by calling {@link #acquire}, and removed by calling {@link
+ * #release}. IBinders are also implicitly removed when they become weakly
+ * reachable. Each IBinder may be added at most once.
*
+ * The {@link #acquired} method is invoked by posting to the specified handler
+ * whenever the size of the watched collection becomes nonzero. The {@link
+ * #released} method is invoked on the specified handler whenever the size of
+ * the watched collection becomes zero.
*/
public abstract class TokenWatcher
{
@@ -59,15 +65,23 @@ public abstract class TokenWatcher
* Record that this token has been acquired. When acquire is called, and
* the current count is 0, the acquired method is called on the given
* handler.
- *
- * @param token An IBinder object. If this token has already been acquired,
- * no action is taken.
+ *
+ * Note that the same {@code token} can only be acquired once. If this
+ * {@code token} has already been acquired, no action is taken. The first
+ * subsequent call to {@link #release} will release this {@code token}
+ * immediately.
+ *
+ * @param token An IBinder object.
* @param tag A string used by the {@link #dump} method for debugging,
* to see who has references.
*/
public void acquire(IBinder token, String tag)
{
synchronized (mTokens) {
+ if (mTokens.containsKey(token)) {
+ return;
+ }
+
// explicitly checked to avoid bogus sendNotification calls because
// of the WeakHashMap and the GC
int oldSize = mTokens.size();
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 8d9630fe5654..e5ad1f47d37d 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -804,7 +804,7 @@ public class StateMachine {
/** State that processed the message */
State msgProcessedState = null;
- if (mIsConstructionCompleted) {
+ if (mIsConstructionCompleted || (mMsg.what == SM_QUIT_CMD)) {
/** Normal path */
msgProcessedState = processMsg(msg);
} else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp
index 737ec47e8ffb..bb916d2431c5 100644
--- a/core/jni/android_os_HwBlob.cpp
+++ b/core/jni/android_os_HwBlob.cpp
@@ -26,6 +26,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <hidl/Status.h>
#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
#include "core_jni_helpers.h"
@@ -349,6 +350,13 @@ static void JHwBlob_native_copyTo ## Suffix ## Array( \
static_cast<const uint8_t *>(blob->data()) + offset)); \
}
+DEFINE_BLOB_ARRAY_COPIER(Int8,jbyte,Byte)
+DEFINE_BLOB_ARRAY_COPIER(Int16,jshort,Short)
+DEFINE_BLOB_ARRAY_COPIER(Int32,jint,Int)
+DEFINE_BLOB_ARRAY_COPIER(Int64,jlong,Long)
+DEFINE_BLOB_ARRAY_COPIER(Float,jfloat,Float)
+DEFINE_BLOB_ARRAY_COPIER(Double,jdouble,Double)
+
static void JHwBlob_native_copyToBoolArray(
JNIEnv *env,
jobject thiz,
@@ -386,13 +394,6 @@ static void JHwBlob_native_copyToBoolArray(
dst = nullptr;
}
-DEFINE_BLOB_ARRAY_COPIER(Int8,jbyte,Byte)
-DEFINE_BLOB_ARRAY_COPIER(Int16,jshort,Short)
-DEFINE_BLOB_ARRAY_COPIER(Int32,jint,Int)
-DEFINE_BLOB_ARRAY_COPIER(Int64,jlong,Long)
-DEFINE_BLOB_ARRAY_COPIER(Float,jfloat,Float)
-DEFINE_BLOB_ARRAY_COPIER(Double,jdouble,Double)
-
#define DEFINE_BLOB_PUTTER(Suffix,Type) \
static void JHwBlob_native_put ## Suffix( \
JNIEnv *env, jobject thiz, jlong offset, Type x) { \
@@ -458,23 +459,17 @@ static void JHwBlob_native_putString(
#define DEFINE_BLOB_ARRAY_PUTTER(Suffix,Type,NewType) \
static void JHwBlob_native_put ## Suffix ## Array( \
JNIEnv *env, jobject thiz, jlong offset, Type ## Array array) { \
+ Scoped ## NewType ## ArrayRO autoArray(env, array); \
\
if (array == nullptr) { \
- jniThrowException(env, "java/lang/NullPointerException", nullptr); \
+ /* NullpointerException already pending */ \
return; \
} \
\
sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); \
\
- jsize len = env->GetArrayLength(array); \
- \
- Type *src = \
- env->Get ## NewType ## ArrayElements(array, nullptr /* isCopy */); \
- \
- status_t err = blob->write(offset, src, len * sizeof(Type)); \
- \
- env->Release ## NewType ## ArrayElements(array, src, 0 /* mode */); \
- src = nullptr; \
+ status_t err = blob->write( \
+ offset, autoArray.get(), autoArray.size() * sizeof(Type)); \
\
if (err != OK) { \
signalExceptionForError(env, err); \
@@ -490,35 +485,28 @@ DEFINE_BLOB_ARRAY_PUTTER(Double,jdouble,Double)
static void JHwBlob_native_putBoolArray(
JNIEnv *env, jobject thiz, jlong offset, jbooleanArray array) {
+ ScopedBooleanArrayRO autoArray(env, array);
if (array == nullptr) {
- jniThrowException(env, "java/lang/NullPointerException", nullptr);
+ /* NullpointerException already pending */
return;
}
sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
- jsize len = env->GetArrayLength(array);
-
- if ((offset + len * sizeof(bool)) > blob->size()) {
+ if ((offset + autoArray.size() * sizeof(bool)) > blob->size()) {
signalExceptionForError(env, -ERANGE);
return;
}
- const jboolean *src =
- env->GetBooleanArrayElements(array, nullptr /* isCopy */);
+ const jboolean *src = autoArray.get();
bool *dst = reinterpret_cast<bool *>(
static_cast<uint8_t *>(blob->data()) + offset);
- for (jsize i = 0; i < len; ++i) {
+ for (size_t i = 0; i < autoArray.size(); ++i) {
dst[i] = src[i];
}
-
- env->ReleaseBooleanArrayElements(
- array, const_cast<jboolean *>(src), 0 /* mode */);
-
- src = nullptr;
}
static void JHwBlob_native_putBlob(
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index cf59a56a13dc..ca5e1e45dcdc 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -22,9 +22,13 @@
#include "android_os_HwParcel.h"
-#include <nativehelper/JNIHelp.h>
+#include <android/hidl/base/1.0/IBase.h>
+#include <android/hidl/base/1.0/BpHwBase.h>
+#include <android/hidl/base/1.0/BnHwBase.h>
#include <android_runtime/AndroidRuntime.h>
#include <hidl/Status.h>
+#include <hidl/HidlTransportSupport.h>
+#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
#include <nativehelper/ScopedLocalRef.h>
@@ -413,6 +417,44 @@ static jboolean JHwRemoteBinder_unlinkToDeath(JNIEnv* env, jobject thiz,
return res;
}
+static sp<hidl::base::V1_0::IBase> toIBase(JNIEnv* env, jclass hwRemoteBinderClazz, jobject jbinder)
+{
+ if (jbinder == nullptr) {
+ return nullptr;
+ }
+ if (!env->IsInstanceOf(jbinder, hwRemoteBinderClazz)) {
+ return nullptr;
+ }
+ sp<JHwRemoteBinder> context = JHwRemoteBinder::GetNativeContext(env, jbinder);
+ sp<hardware::IBinder> cbinder = context->getBinder();
+ return hardware::fromBinder<hidl::base::V1_0::IBase, hidl::base::V1_0::BpHwBase,
+ hidl::base::V1_0::BnHwBase>(cbinder);
+}
+
+// equals iff other is also a non-null android.os.HwRemoteBinder object
+// and getBinder() returns the same object.
+// In particular, if other is an android.os.HwBinder object (for stubs) then
+// it returns false.
+static jboolean JHwRemoteBinder_equals(JNIEnv* env, jobject thiz, jobject other)
+{
+ if (env->IsSameObject(thiz, other)) {
+ return true;
+ }
+ if (other == NULL) {
+ return false;
+ }
+
+ ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
+
+ return hardware::interfacesEqual(toIBase(env, clazz.get(), thiz), toIBase(env, clazz.get(), other));
+}
+
+static jint JHwRemoteBinder_hashCode(JNIEnv* env, jobject thiz) {
+ jlong longHash = reinterpret_cast<jlong>(
+ JHwRemoteBinder::GetNativeContext(env, thiz)->getBinder().get());
+ return static_cast<jint>(longHash ^ (longHash >> 32)); // See Long.hashCode()
+}
+
static JNINativeMethod gMethods[] = {
{ "native_init", "()J", (void *)JHwRemoteBinder_native_init },
@@ -430,6 +472,11 @@ static JNINativeMethod gMethods[] = {
{"unlinkToDeath",
"(Landroid/os/IHwBinder$DeathRecipient;)Z",
(void*)JHwRemoteBinder_unlinkToDeath},
+
+ {"equals", "(Ljava/lang/Object;)Z",
+ (void*)JHwRemoteBinder_equals},
+
+ {"hashCode", "()I", (void*)JHwRemoteBinder_hashCode},
};
namespace android {
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 9f8d2880d8bf..560c38486bb9 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -162,6 +162,7 @@ static std::atomic<uint32_t> gCollectedAtRefs(0);
// Garbage collect if we've allocated at least GC_INTERVAL refs since the last time.
// TODO: Consider removing this completely. We should no longer be generating GlobalRefs
// that are reclaimed as a result of GC action.
+__attribute__((no_sanitize("unsigned-integer-overflow")))
static void gcIfManyNewRefs(JNIEnv* env)
{
uint32_t totalRefs = gNumLocalRefsCreated.load(std::memory_order_relaxed)
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 40fb6e83e94d..e291ea6cc856 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2402,7 +2402,13 @@
<bool name="config_networkSamplingWakesDevice">true</bool>
- <string-array translatable="false" name="config_cdma_home_system" />
+ <!-- Home (non-roaming) values for CDMA roaming indicator.
+ Carriers can override this table by resource overlay. If not,
+ the default values come from 3GPP2 C.R1001 table
+ 8.1-1. Enhanced Roaming Indicator Number Assignments -->
+ <string-array translatable="false" name="config_cdma_home_system">
+ <item>1</item>
+ </string-array>
<!--From SmsMessage-->
<!--Support decoding the user data payload as pack GSM 8-bit (a GSM alphabet
diff --git a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
index eb2a5165dc4f..76aa93f7e8be 100644
--- a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
@@ -24,6 +24,7 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.Suppress;
import com.android.internal.util.State;
@@ -343,6 +344,100 @@ public class StateMachineTest extends TestCase {
}
/**
+ * Tests {@link StateMachine#quitNow()} immediately after {@link StateMachine#start()}.
+ */
+ class StateMachineQuitNowAfterStartTest extends StateMachine {
+ Collection<LogRec> mLogRecs;
+
+ StateMachineQuitNowAfterStartTest(String name, Looper looper) {
+ super(name, looper);
+ mThisSm = this;
+ setDbg(DBG);
+
+ // Setup state machine with 1 state
+ addState(mS1);
+
+ // Set the initial state
+ setInitialState(mS1);
+ }
+
+ @Override
+ public void onQuitting() {
+ tlog("onQuitting");
+ addLogRec(ON_QUITTING);
+ mLogRecs = mThisSm.copyLogRecs();
+ synchronized (mThisSm) {
+ mThisSm.notifyAll();
+ }
+ }
+
+ class S1 extends State {
+ @Override
+ public void enter() {
+ tlog("S1.enter");
+ addLogRec(ENTER);
+ }
+ @Override
+ public void exit() {
+ tlog("S1.exit");
+ addLogRec(EXIT);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch(message.what) {
+ // Sleep and assume the other messages will be queued up.
+ case TEST_CMD_1: {
+ tlog("TEST_CMD_1");
+ sleep(500);
+ break;
+ }
+ default: {
+ tlog("default what=" + message.what);
+ break;
+ }
+ }
+ return HANDLED;
+ }
+ }
+
+ private StateMachineQuitNowAfterStartTest mThisSm;
+ private S1 mS1 = new S1();
+ }
+
+ /**
+ * When quitNow() is called immediately after start(), the QUIT_CMD gets processed
+ * before the INIT_CMD. This test ensures that the StateMachine can gracefully handle
+ * this sequencing of messages (QUIT before INIT).
+ */
+ @SmallTest
+ public void testStateMachineQuitNowAfterStart() throws Exception {
+ if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
+
+ TestLooper testLooper = new TestLooper();
+ StateMachineQuitNowAfterStartTest smQuitNowAfterStartTest =
+ new StateMachineQuitNowAfterStartTest(
+ "smQuitNowAfterStartTest", testLooper.getLooper());
+ smQuitNowAfterStartTest.start();
+ smQuitNowAfterStartTest.quitNow();
+ if (smQuitNowAfterStartTest.isDbg()) tlog("testStateMachineQuitNowAfterStart E");
+
+ testLooper.dispatchAll();
+ dumpLogRecs(smQuitNowAfterStartTest.mLogRecs);
+ assertEquals(2, smQuitNowAfterStartTest.mLogRecs.size());
+
+ LogRec lr;
+ Iterator<LogRec> itr = smQuitNowAfterStartTest.mLogRecs.iterator();
+ lr = itr.next();
+ assertEquals(EXIT, lr.getInfo());
+ assertEquals(smQuitNowAfterStartTest.mS1, lr.getState());
+
+ lr = itr.next();
+ assertEquals(ON_QUITTING, lr.getInfo());
+
+ if (smQuitNowAfterStartTest.isDbg()) tlog("testStateMachineQuitNowAfterStart X");
+ }
+
+ /**
* Test enter/exit can use transitionTo
*/
class StateMachineEnterExitTransitionToTest extends StateMachine {
diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
index 5abfc8ee917e..5d243da2097c 100644
--- a/libs/androidfw/ZipUtils.cpp
+++ b/libs/androidfw/ZipUtils.cpp
@@ -20,10 +20,11 @@
#define LOG_TAG "ziputil"
+#include "android-base/file.h"
#include <androidfw/ZipUtils.h>
-#include <androidfw/ZipFileRO.h>
#include <utils/Log.h>
#include <utils/Compat.h>
+#include <ziparchive/zip_archive.h>
#include <stdlib.h>
#include <string.h>
@@ -33,211 +34,121 @@
using namespace android;
-static inline unsigned long get4LE(const unsigned char* buf) {
- return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
-}
-
-
-static const unsigned long kReadBufSize = 32768;
-
-/*
- * Utility function that expands zip/gzip "deflate" compressed data
- * into a buffer.
- *
- * (This is a clone of the previous function, but it takes a FILE* instead
- * of an fd. We could pass fileno(fd) to the above, but we can run into
- * trouble when "fp" has a different notion of what fd's file position is.)
- *
- * "fp" is an open file positioned at the start of the "deflate" data
- * "buf" must hold at least "uncompressedLen" bytes.
- */
-/*static*/ template<typename T> bool inflateToBuffer(T& reader, void* buf,
- long uncompressedLen, long compressedLen)
-{
- bool result = false;
-
- z_stream zstream;
- int zerr;
- unsigned long compRemaining;
-
- assert(uncompressedLen >= 0);
- assert(compressedLen >= 0);
-
- compRemaining = compressedLen;
-
- /*
- * Initialize the zlib stream.
- */
- memset(&zstream, 0, sizeof(zstream));
- zstream.zalloc = Z_NULL;
- zstream.zfree = Z_NULL;
- zstream.opaque = Z_NULL;
- zstream.next_in = NULL;
- zstream.avail_in = 0;
- zstream.next_out = (Bytef*) buf;
- zstream.avail_out = uncompressedLen;
- zstream.data_type = Z_UNKNOWN;
-
- /*
- * Use the undocumented "negative window bits" feature to tell zlib
- * that there's no zlib header waiting for it.
- */
- zerr = inflateInit2(&zstream, -MAX_WBITS);
- if (zerr != Z_OK) {
- if (zerr == Z_VERSION_ERROR) {
- ALOGE("Installed zlib is not compatible with linked version (%s)\n",
- ZLIB_VERSION);
- } else {
- ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
- }
- goto bail;
+// TODO: This can go away once the only remaining usage in aapt goes away.
+class FileReader : public zip_archive::Reader {
+ public:
+ FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) {
}
- /*
- * Loop while we have data.
- */
- do {
- unsigned long getSize;
-
- /* read as much as we can */
- if (zstream.avail_in == 0) {
- getSize = (compRemaining > kReadBufSize) ?
- kReadBufSize : compRemaining;
- ALOGV("+++ reading %ld bytes (%ld left)\n",
- getSize, compRemaining);
-
- unsigned char* nextBuffer = NULL;
- const unsigned long nextSize = reader.read(&nextBuffer, getSize);
-
- if (nextSize < getSize || nextBuffer == NULL) {
- ALOGD("inflate read failed (%ld vs %ld)\n", nextSize, getSize);
- goto z_bail;
+ bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+ // Data is usually requested sequentially, so this helps avoid pointless
+ // fseeks every time we perform a read. There's an impedence mismatch
+ // here because the original API was designed around pread and pwrite.
+ if (offset != mCurrentOffset) {
+ if (fseek(mFp, offset, SEEK_SET) != 0) {
+ return false;
}
- compRemaining -= nextSize;
-
- zstream.next_in = nextBuffer;
- zstream.avail_in = nextSize;
+ mCurrentOffset = offset;
}
- /* uncompress the data */
- zerr = inflate(&zstream, Z_NO_FLUSH);
- if (zerr != Z_OK && zerr != Z_STREAM_END) {
- ALOGD("zlib inflate call failed (zerr=%d)\n", zerr);
- goto z_bail;
+ size_t read = fread(buf, 1, len, mFp);
+ if (read != len) {
+ return false;
}
- /* output buffer holds all, so no need to write the output */
- } while (zerr == Z_OK);
-
- assert(zerr == Z_STREAM_END); /* other errors should've been caught */
-
- if ((long) zstream.total_out != uncompressedLen) {
- ALOGW("Size mismatch on inflated file (%ld vs %ld)\n",
- zstream.total_out, uncompressedLen);
- goto z_bail;
+ mCurrentOffset += read;
+ return true;
}
- // success!
- result = true;
-
-z_bail:
- inflateEnd(&zstream); /* free up any allocated structures */
-
-bail:
- return result;
-}
-
-class FileReader {
-public:
- explicit FileReader(FILE* fp) :
- mFp(fp), mReadBuf(new unsigned char[kReadBufSize])
- {
- }
+ private:
+ FILE* mFp;
+ mutable uint32_t mCurrentOffset;
+};
- ~FileReader() {
- delete[] mReadBuf;
- }
+class FdReader : public zip_archive::Reader {
+ public:
+ explicit FdReader(int fd) : mFd(fd) {
+ }
- long read(unsigned char** nextBuffer, long readSize) const {
- *nextBuffer = mReadBuf;
- return fread(mReadBuf, 1, readSize, mFp);
- }
+ bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+ return android::base::ReadFullyAtOffset(mFd, buf, len, static_cast<off_t>(offset));
+ }
- FILE* mFp;
- unsigned char* mReadBuf;
+ private:
+ const int mFd;
};
-class FdReader {
-public:
- explicit FdReader(int fd) :
- mFd(fd), mReadBuf(new unsigned char[kReadBufSize])
- {
- }
+class BufferReader : public zip_archive::Reader {
+ public:
+ BufferReader(const void* input, size_t inputSize) : Reader(),
+ mInput(reinterpret_cast<const uint8_t*>(input)),
+ mInputSize(inputSize) {
+ }
- ~FdReader() {
- delete[] mReadBuf;
- }
+ bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+ if (offset + len > mInputSize) {
+ return false;
+ }
- long read(unsigned char** nextBuffer, long readSize) const {
- *nextBuffer = mReadBuf;
- return TEMP_FAILURE_RETRY(::read(mFd, mReadBuf, readSize));
- }
+ memcpy(buf, mInput + offset, len);
+ return true;
+ }
- int mFd;
- unsigned char* mReadBuf;
+ private:
+ const uint8_t* mInput;
+ const size_t mInputSize;
};
-class BufferReader {
-public:
- BufferReader(void* input, size_t inputSize) :
- mInput(reinterpret_cast<unsigned char*>(input)),
- mInputSize(inputSize),
- mBufferReturned(false)
- {
+class BufferWriter : public zip_archive::Writer {
+ public:
+ BufferWriter(void* output, size_t outputSize) : Writer(),
+ mOutput(reinterpret_cast<uint8_t*>(output)), mOutputSize(outputSize), mBytesWritten(0) {
}
- long read(unsigned char** nextBuffer, long /*readSize*/) {
- if (!mBufferReturned) {
- mBufferReturned = true;
- *nextBuffer = mInput;
- return mInputSize;
+ bool Append(uint8_t* buf, size_t bufSize) override {
+ if (mBytesWritten + bufSize > mOutputSize) {
+ return false;
}
- *nextBuffer = NULL;
- return 0;
+ memcpy(mOutput + mBytesWritten, buf, bufSize);
+ mBytesWritten += bufSize;
+ return true;
}
- unsigned char* mInput;
- const size_t mInputSize;
- bool mBufferReturned;
+ private:
+ uint8_t* const mOutput;
+ const size_t mOutputSize;
+ size_t mBytesWritten;
};
/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf,
long uncompressedLen, long compressedLen)
{
FileReader reader(fp);
- return ::inflateToBuffer<FileReader>(reader, buf,
- uncompressedLen, compressedLen);
+ BufferWriter writer(buf, uncompressedLen);
+ return (zip_archive::Inflate(reader, compressedLen, uncompressedLen, &writer, nullptr) == 0);
}
/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf,
long uncompressedLen, long compressedLen)
{
FdReader reader(fd);
- return ::inflateToBuffer<FdReader>(reader, buf,
- uncompressedLen, compressedLen);
+ BufferWriter writer(buf, uncompressedLen);
+ return (zip_archive::Inflate(reader, compressedLen, uncompressedLen, &writer, nullptr) == 0);
}
-/*static*/ bool ZipUtils::inflateToBuffer(void* in, void* buf,
+/*static*/ bool ZipUtils::inflateToBuffer(const void* in, void* buf,
long uncompressedLen, long compressedLen)
{
BufferReader reader(in, compressedLen);
- return ::inflateToBuffer<BufferReader>(reader, buf,
- uncompressedLen, compressedLen);
+ BufferWriter writer(buf, uncompressedLen);
+ return (zip_archive::Inflate(reader, compressedLen, uncompressedLen, &writer, nullptr) == 0);
}
-
+static inline unsigned long get4LE(const unsigned char* buf) {
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+}
/*
* Look at the contents of a gzip archive. We want to know where the
@@ -275,7 +186,7 @@ public:
/* quick sanity checks */
if (method == EOF || flags == EOF)
return false;
- if (method != ZipFileRO::kCompressDeflated)
+ if (method != kCompressDeflated)
return false;
/* skip over 4 bytes of mod time, 1 byte XFL, 1 byte OS */
diff --git a/libs/androidfw/include/androidfw/ZipUtils.h b/libs/androidfw/include/androidfw/ZipUtils.h
index 55575d774522..4d35e992cc89 100644
--- a/libs/androidfw/include/androidfw/ZipUtils.h
+++ b/libs/androidfw/include/androidfw/ZipUtils.h
@@ -40,7 +40,7 @@ public:
long compressedLen);
static bool inflateToBuffer(int fd, void* buf, long uncompressedLen,
long compressedLen);
- static bool inflateToBuffer(void *in, void* buf, long uncompressedLen,
+ static bool inflateToBuffer(const void *in, void* buf, long uncompressedLen,
long compressedLen);
/*
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index da6e26e17122..73ac05738f0f 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -53,7 +53,8 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
private final static boolean DEBUG_DEXOPT = true;
// The synthetic library dependencies denoting "no checks."
- private final static String[] NO_LIBRARIES = new String[] { "&" };
+ private final static String[] NO_LIBRARIES =
+ new String[] { PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK };
// The amount of "available" (free - low threshold) space necessary at the start of an OTA to
// not bulk-delete unused apps' odex files.
@@ -325,7 +326,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
mPackageManagerService.getDexManager().dexoptSecondaryDex(
new DexoptOptions(pkg.packageName, compilationReason,
DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX |
- DexoptOptions.DEXOPT_BOOT_COMPLETE));
+ DexoptOptions.DEXOPT_BOOT_COMPLETE));
return commands;
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index dfa828df5236..ef015e7e418b 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -40,6 +40,7 @@ import com.android.server.pm.dex.PackageDexUsage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -158,16 +159,37 @@ public class PackageDexOptimizer {
// Get the class loader context dependencies.
// For each code path in the package, this array contains the class loader context that
// needs to be passed to dexopt in order to ensure correct optimizations.
+ boolean[] pathsWithCode = new boolean[paths.size()];
+ pathsWithCode[0] = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ for (int i = 1; i < paths.size(); i++) {
+ pathsWithCode[i] = (pkg.splitFlags[i - 1] & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ }
String[] classLoaderContexts = DexoptUtils.getClassLoaderContexts(
- pkg.applicationInfo, sharedLibraries);
+ pkg.applicationInfo, sharedLibraries, pathsWithCode);
+
+ // Sanity check that we do not call dexopt with inconsistent data.
+ if (paths.size() != classLoaderContexts.length) {
+ String[] splitCodePaths = pkg.applicationInfo.getSplitCodePaths();
+ throw new IllegalStateException("Inconsistent information "
+ + "between PackageParser.Package and its ApplicationInfo. "
+ + "pkg.getAllCodePaths=" + paths
+ + " pkg.applicationInfo.getBaseCodePath=" + pkg.applicationInfo.getBaseCodePath()
+ + " pkg.applicationInfo.getSplitCodePaths="
+ + (splitCodePaths == null ? "null" : Arrays.toString(splitCodePaths)));
+ }
int result = DEX_OPT_SKIPPED;
for (int i = 0; i < paths.size(); i++) {
// Skip paths that have no code.
- if ((i == 0 && (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) ||
- (i != 0 && (pkg.splitFlags[i - 1] & ApplicationInfo.FLAG_HAS_CODE) == 0)) {
+ if (!pathsWithCode[i]) {
continue;
}
+ if (classLoaderContexts[i] == null) {
+ throw new IllegalStateException("Inconsistent information in the "
+ + "package structure. A split is marked to contain code "
+ + "but has no dependency listed. Index=" + i + " path=" + paths.get(i));
+ }
+
// Append shared libraries with split dependencies for this split.
String path = paths.get(i);
if (options.getSplitName() != null) {
@@ -187,7 +209,7 @@ public class PackageDexOptimizer {
// Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
// flags.
- final int dexoptFlags = getDexFlags(pkg, compilerFilter, options.isBootComplete());
+ final int dexoptFlags = getDexFlags(pkg, compilerFilter, options);
for (String dexCodeIsa : dexCodeInstructionSets) {
int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
@@ -327,8 +349,7 @@ public class PackageDexOptimizer {
dexUseInfo.isUsedByOtherApps());
// Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
// Secondary dex files are currently not compiled at boot.
- int dexoptFlags = getDexFlags(info, compilerFilter, /* bootComplete */ true)
- | DEXOPT_SECONDARY_DEX;
+ int dexoptFlags = getDexFlags(info, compilerFilter, options) | DEXOPT_SECONDARY_DEX;
// Check the app storage and add the appropriate flags.
if (info.deviceProtectedDataDir != null &&
FileUtils.contains(info.deviceProtectedDataDir, path)) {
@@ -345,18 +366,13 @@ public class PackageDexOptimizer {
+ " dexoptFlags=" + printDexoptFlags(dexoptFlags)
+ " target-filter=" + compilerFilter);
- String classLoaderContext;
- if (dexUseInfo.isUnknownClassLoaderContext() ||
- dexUseInfo.isUnsupportedClassLoaderContext() ||
- dexUseInfo.isVariableClassLoaderContext()) {
- // If we have an unknown (not yet set), unsupported (custom class loaders), or a
- // variable class loader chain, compile without a context and mark the oat file with
- // SKIP_SHARED_LIBRARY_CHECK. Note that his might lead to a incorrect compilation.
- // TODO(calin): We should just extract in this case.
- classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
- } else {
- classLoaderContext = dexUseInfo.getClassLoaderContext();
- }
+ // TODO(calin): b/64530081 b/66984396. Use SKIP_SHARED_LIBRARY_CHECK for the context
+ // (instead of dexUseInfo.getClassLoaderContext()) in order to compile secondary dex files
+ // in isolation (and avoid to extract/verify the main apk if it's in the class path).
+ // Note this trades correctness for performance since the resulting slow down is
+ // unacceptable in some cases until b/64530081 is fixed.
+ String classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
+
try {
for (String isa : dexUseInfo.getLoaderIsas()) {
// Reuse the same dexopt path as for the primary apks. We don't need all the
@@ -416,7 +432,7 @@ public class PackageDexOptimizer {
}
if (useInfo.isUsedByOtherApps(path)) {
- pw.println("used be other apps: " + useInfo.getLoadingPackages(path));
+ pw.println("used by other apps: " + useInfo.getLoadingPackages(path));
}
Map<String, PackageDexUsage.DexUseInfo> dexUseInfoMap = useInfo.getDexUseInfoMap();
@@ -429,19 +445,10 @@ public class PackageDexOptimizer {
PackageDexUsage.DexUseInfo dexUseInfo = e.getValue();
pw.println(dex);
pw.increaseIndent();
- for (String isa : dexUseInfo.getLoaderIsas()) {
- String status = null;
- try {
- status = DexFile.getDexFileStatus(path, isa);
- } catch (IOException ioe) {
- status = "[Exception]: " + ioe.getMessage();
- }
- pw.println(isa + ": " + status);
- }
-
+ // TODO(calin): get the status of the oat file (needs installd call)
pw.println("class loader context: " + dexUseInfo.getClassLoaderContext());
if (dexUseInfo.isUsedByOtherApps()) {
- pw.println("used be other apps: " + dexUseInfo.getLoadingPackages());
+ pw.println("used by other apps: " + dexUseInfo.getLoadingPackages());
}
pw.decreaseIndent();
}
@@ -465,8 +472,9 @@ public class PackageDexOptimizer {
}
if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) {
- // If the dex files is used by other apps, we cannot use profile-guided compilation.
- return getNonProfileGuidedCompilerFilter(targetCompilerFilter);
+ // If the dex files is used by other apps, apply the shared filter.
+ return PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
+ PackageManagerService.REASON_SHARED);
}
return targetCompilerFilter;
@@ -477,11 +485,11 @@ public class PackageDexOptimizer {
* filter.
*/
private int getDexFlags(PackageParser.Package pkg, String compilerFilter,
- boolean bootComplete) {
- return getDexFlags(pkg.applicationInfo, compilerFilter, bootComplete);
+ DexoptOptions options) {
+ return getDexFlags(pkg.applicationInfo, compilerFilter, options);
}
- private int getDexFlags(ApplicationInfo info, String compilerFilter, boolean bootComplete) {
+ private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
int flags = info.flags;
boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
// Profile guide compiled oat files should not be public.
@@ -492,7 +500,8 @@ public class PackageDexOptimizer {
(isPublic ? DEXOPT_PUBLIC : 0)
| (debuggable ? DEXOPT_DEBUGGABLE : 0)
| profileFlag
- | (bootComplete ? DEXOPT_BOOTCOMPLETE : 0);
+ | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
+ | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0);
return adjustDexoptFlags(dexFlags);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a8ea4eaee4fd..242a76dbb63f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -562,8 +562,9 @@ public class PackageManagerService extends IPackageManager.Stub
public static final int REASON_BACKGROUND_DEXOPT = 3;
public static final int REASON_AB_OTA = 4;
public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 5;
+ public static final int REASON_SHARED = 6;
- public static final int REASON_LAST = REASON_INACTIVE_PACKAGE_DOWNGRADE;
+ public static final int REASON_LAST = REASON_SHARED;
/** All dangerous permission names in the same order as the events in MetricsEvent */
private static final List<String> ALL_DANGEROUS_PERMISSIONS = Arrays.asList(
@@ -18263,36 +18264,6 @@ public class PackageManagerService extends IPackageManager.Stub
Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
}
}
-
- // dexopt can take some time to complete, so, for instant apps, we skip this
- // step during installation. Instead, we'll take extra time the first time the
- // instant app starts. It's preferred to do it this way to provide continuous
- // progress to the user instead of mysteriously blocking somewhere in the
- // middle of running an instant app. The default behaviour can be overridden
- // via gservices.
- if (!instantApp || Global.getInt(
- mContext.getContentResolver(), Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
- // Do not run PackageDexOptimizer through the local performDexOpt
- // method because `pkg` may not be in `mPackages` yet.
- //
- // Also, don't fail application installs if the dexopt step fails.
- DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName,
- REASON_INSTALL,
- DexoptOptions.DEXOPT_BOOT_COMPLETE);
- mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
- null /* instructionSets */,
- getOrCreateCompilerPackageStats(pkg),
- mDexManager.getPackageUseInfoOrDefault(pkg.packageName),
- dexoptOptions);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- // Notify BackgroundDexOptService that the package has been changed.
- // If this is an update of a package which used to fail to compile,
- // BDOS will remove it from its blacklist.
- // TODO: Layering violation
- BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
}
if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
@@ -18300,6 +18271,50 @@ public class PackageManagerService extends IPackageManager.Stub
return;
}
+ // Verify if we need to dexopt the app.
+ //
+ // NOTE: it is *important* to call dexopt after doRename which will sync the
+ // package data from PackageParser.Package and its corresponding ApplicationInfo.
+ //
+ // We only need to dexopt if the package meets ALL of the following conditions:
+ // 1) it is not forward locked.
+ // 2) it is not on on an external ASEC container.
+ // 3) it is not an instant app or if it is then dexopt is enabled via gservices.
+ //
+ // Note that we do not dexopt instant apps by default. dexopt can take some time to
+ // complete, so we skip this step during installation. Instead, we'll take extra time
+ // the first time the instant app starts. It's preferred to do it this way to provide
+ // continuous progress to the useur instead of mysteriously blocking somewhere in the
+ // middle of running an instant app. The default behaviour can be overridden
+ // via gservices.
+ final boolean performDexopt = !forwardLocked
+ && !pkg.applicationInfo.isExternalAsec()
+ && (!instantApp || Global.getInt(mContext.getContentResolver(),
+ Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0);
+
+ if (performDexopt) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+ // Do not run PackageDexOptimizer through the local performDexOpt
+ // method because `pkg` may not be in `mPackages` yet.
+ //
+ // Also, don't fail application installs if the dexopt step fails.
+ DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName,
+ REASON_INSTALL,
+ DexoptOptions.DEXOPT_BOOT_COMPLETE);
+ mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
+ null /* instructionSets */,
+ getOrCreateCompilerPackageStats(pkg),
+ mDexManager.getPackageUseInfoOrDefault(pkg.packageName),
+ dexoptOptions);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ // Notify BackgroundDexOptService that the package has been changed.
+ // If this is an update of a package which used to fail to compile,
+ // BackgroundDexOptService will remove it from its blacklist.
+ // TODO: Layering violation
+ BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
+
startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 0a9480b4bbe8..781216c3c43f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -26,14 +26,19 @@ import dalvik.system.DexFile;
public class PackageManagerServiceCompilerMapping {
// Names for compilation reasons.
static final String REASON_STRINGS[] = {
- "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive"
+ "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive", "shared"
};
+ static final int REASON_SHARED_INDEX = 6;
+
// Static block to ensure the strings array is of the right length.
static {
if (PackageManagerService.REASON_LAST + 1 != REASON_STRINGS.length) {
throw new IllegalStateException("REASON_STRINGS not correct");
}
+ if (!"shared".equals(REASON_STRINGS[REASON_SHARED_INDEX])) {
+ throw new IllegalStateException("REASON_STRINGS not correct because of shared index");
+ }
}
private static String getSystemPropertyName(int reason) {
@@ -54,6 +59,8 @@ public class PackageManagerServiceCompilerMapping {
// vendor.
if ("pm.dexopt.inactive".equals(sysPropName)) {
sysPropValue = "verify";
+ } else if ("pm.dexopt.shared".equals(sysPropName)) {
+ sysPropValue = "speed";
} else {
sysPropValue = SystemProperties.get(sysPropName);
}
@@ -61,11 +68,18 @@ public class PackageManagerServiceCompilerMapping {
!DexFile.isValidCompilerFilter(sysPropValue)) {
throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
+ "(reason " + REASON_STRINGS[reason] + ")");
+ } else if (!isFilterAllowedForReason(reason, sysPropValue)) {
+ throw new IllegalStateException("Value \"" + sysPropValue +"\" not allowed "
+ + "(reason " + REASON_STRINGS[reason] + ")");
}
return sysPropValue;
}
+ private static boolean isFilterAllowedForReason(int reason, String filter) {
+ return reason != REASON_SHARED_INDEX || !DexFile.isProfileGuidedCompilerFilter(filter);
+ }
+
// Check that the properties are set and valid.
// Note: this is done in a separate method so this class can be statically initialized.
static void checkProperties() {
diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
index bc8bf5e1fc4c..c23b03153e58 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
@@ -20,6 +20,8 @@ import android.content.pm.ApplicationInfo;
import android.util.Slog;
import android.util.SparseArray;
+import com.android.server.pm.PackageDexOptimizer;
+
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
@@ -33,7 +35,9 @@ public final class DexoptUtils {
/**
* Creates the class loader context dependencies for each of the application code paths.
* The returned array contains the class loader contexts that needs to be passed to dexopt in
- * order to ensure correct optimizations.
+ * order to ensure correct optimizations. "Code" paths with no actual code, as specified by
+ * {@param pathsWithCode}, are ignored and will have null as their context in the returned array
+ * (configuration splits are an example of paths without code).
*
* A class loader context describes how the class loader chain should be built by dex2oat
* in order to ensure that classes are resolved during compilation as they would be resolved
@@ -58,7 +62,8 @@ public final class DexoptUtils {
* {@link android.app.LoadedApk#makePaths(
* android.app.ActivityThread, boolean, ApplicationInfo, List, List)}.
*/
- public static String[] getClassLoaderContexts(ApplicationInfo info, String[] sharedLibraries) {
+ public static String[] getClassLoaderContexts(ApplicationInfo info,
+ String[] sharedLibraries, boolean[] pathsWithCode) {
// The base class loader context contains only the shared library.
String sharedLibrariesClassPath = encodeClasspath(sharedLibraries);
String baseApkContextClassLoader = encodeClassLoader(
@@ -84,7 +89,7 @@ public final class DexoptUtils {
// Index 0 is the class loaded context for the base apk.
// Index `i` is the class loader context encoding for split `i`.
String[] classLoaderContexts = new String[/*base apk*/ 1 + splitRelativeCodePaths.length];
- classLoaderContexts[0] = baseApkContextClassLoader;
+ classLoaderContexts[0] = pathsWithCode[0] ? baseApkContextClassLoader : null;
if (!info.requestsIsolatedSplitLoading() || info.splitDependencies == null) {
// If the app didn't request for the splits to be loaded in isolation or if it does not
@@ -92,7 +97,15 @@ public final class DexoptUtils {
// apk class loader (in the order of their definition).
String classpath = sharedLibrariesAndBaseClassPath;
for (int i = 1; i < classLoaderContexts.length; i++) {
- classLoaderContexts[i] = encodeClassLoader(classpath, "dalvik.system.PathClassLoader");
+ classLoaderContexts[i] = pathsWithCode[i]
+ ? encodeClassLoader(classpath, "dalvik.system.PathClassLoader") : null;
+ // Note that the splits with no code are not removed from the classpath computation.
+ // i.e. split_n might get the split_n-1 in its classpath dependency even
+ // if split_n-1 has no code.
+ // The splits with no code do not matter for the runtime which ignores
+ // apks without code when doing the classpath checks. As such we could actually
+ // filter them but we don't do it in order to keep consistency with how the apps
+ // are loaded.
classpath = encodeClasspath(classpath, splitRelativeCodePaths[i - 1]);
}
} else {
@@ -114,9 +127,17 @@ public final class DexoptUtils {
String splitDependencyOnBase = encodeClassLoader(
sharedLibrariesAndBaseClassPath, "dalvik.system.PathClassLoader");
SparseArray<int[]> splitDependencies = info.splitDependencies;
+
+ // Note that not all splits have dependencies (e.g. configuration splits)
+ // The splits without dependencies will have classLoaderContexts[config_split_index]
+ // set to null after this step.
for (int i = 1; i < splitDependencies.size(); i++) {
- getParentDependencies(splitDependencies.keyAt(i), splitClassLoaderEncodingCache,
- splitDependencies, classLoaderContexts, splitDependencyOnBase);
+ int splitIndex = splitDependencies.keyAt(i);
+ if (pathsWithCode[splitIndex]) {
+ // Compute the class loader context only for the splits with code.
+ getParentDependencies(splitIndex, splitClassLoaderEncodingCache,
+ splitDependencies, classLoaderContexts, splitDependencyOnBase);
+ }
}
// At this point classLoaderContexts contains only the parent dependencies.
@@ -124,8 +145,17 @@ public final class DexoptUtils {
// come first in the context.
for (int i = 1; i < classLoaderContexts.length; i++) {
String splitClassLoader = encodeClassLoader("", "dalvik.system.PathClassLoader");
- classLoaderContexts[i] = encodeClassLoaderChain(
- splitClassLoader, classLoaderContexts[i]);
+ if (pathsWithCode[i]) {
+ // If classLoaderContexts[i] is null it means that the split does not have
+ // any dependency. In this case its context equals its declared class loader.
+ classLoaderContexts[i] = classLoaderContexts[i] == null
+ ? splitClassLoader
+ : encodeClassLoaderChain(splitClassLoader, classLoaderContexts[i]);
+ } else {
+ // This is a split without code, it has no dependency and it is not compiled.
+ // Its context will be null.
+ classLoaderContexts[i] = null;
+ }
}
}
@@ -208,10 +238,15 @@ public final class DexoptUtils {
/**
* Encodes a single class loader dependency starting from {@param path} and
* {@param classLoaderName}.
+ * When classpath is {@link PackageDexOptimizer#SKIP_SHARED_LIBRARY_CHECK}, the method returns
+ * the same. This special property is used only during OTA.
* NOTE: Keep this in sync with the dexopt expectations! Right now that is either "PCL[path]"
* for a PathClassLoader or "DLC[path]" for a DelegateLastClassLoader.
*/
- private static String encodeClassLoader(String classpath, String classLoaderName) {
+ /*package*/ static String encodeClassLoader(String classpath, String classLoaderName) {
+ if (classpath.equals(PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK)) {
+ return classpath;
+ }
String classLoaderDexoptEncoding = classLoaderName;
if ("dalvik.system.PathClassLoader".equals(classLoaderName)) {
classLoaderDexoptEncoding = "PCL";
@@ -223,10 +258,17 @@ public final class DexoptUtils {
/**
* Links to dependencies together in a format accepted by dexopt.
+ * For the special case when either of cl1 or cl2 equals
+ * {@link PackageDexOptimizer#SKIP_SHARED_LIBRARY_CHECK}, the method returns the same. This
+ * property is used only during OTA.
* NOTE: Keep this in sync with the dexopt expectations! Right now that is a list of split
* dependencies {@see encodeClassLoader} separated by ';'.
*/
- private static String encodeClassLoaderChain(String cl1, String cl2) {
+ /*package*/ static String encodeClassLoaderChain(String cl1, String cl2) {
+ if (cl1.equals(PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK) ||
+ cl2.equals(PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK)) {
+ return PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK;
+ }
if (cl1.isEmpty()) return cl2;
if (cl2.isEmpty()) return cl1;
return cl1 + ";" + cl2;
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
index ff7bd723d23d..34dc1adf3430 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
@@ -16,10 +16,14 @@
package com.android.server.pm.dex;
+import com.android.server.pm.PackageDexOptimizer;
+
+import static com.android.server.pm.PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.content.pm.ApplicationInfo;
import android.support.test.filters.SmallTest;
@@ -46,24 +50,46 @@ public class DexoptUtilsTest {
private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
DelegateLastClassLoader.class.getName();
- private ApplicationInfo createMockApplicationInfo(String baseClassLoader, boolean addSplits,
+ private static class TestData {
+ ApplicationInfo info;
+ boolean[] pathsWithCode;
+ }
+
+ private TestData createMockApplicationInfo(String baseClassLoader, boolean addSplits,
boolean addSplitDependencies) {
ApplicationInfo ai = new ApplicationInfo();
String codeDir = "/data/app/mock.android.com";
ai.setBaseCodePath(codeDir + "/base.dex");
ai.privateFlags = ai.privateFlags | ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING;
+ boolean[] pathsWithCode;
+ if (!addSplits) {
+ pathsWithCode = new boolean[] {true};
+ } else {
+ pathsWithCode = new boolean[9];
+ Arrays.fill(pathsWithCode, true);
+ pathsWithCode[7] = false; // config split
- if (addSplits) {
ai.setSplitCodePaths(new String[]{
codeDir + "/base-1.dex",
codeDir + "/base-2.dex",
codeDir + "/base-3.dex",
codeDir + "/base-4.dex",
codeDir + "/base-5.dex",
- codeDir + "/base-6.dex"});
-
+ codeDir + "/base-6.dex",
+ codeDir + "/config-split-7.dex",
+ codeDir + "/feature-no-deps.dex"});
+
+ String[] splitClassLoaderNames = new String[]{
+ PATH_CLASS_LOADER_NAME,
+ PATH_CLASS_LOADER_NAME,
+ PATH_CLASS_LOADER_NAME,
+ PATH_CLASS_LOADER_NAME,
+ PATH_CLASS_LOADER_NAME,
+ null, // A null class loader name should default to PathClassLoader.
+ null, // The config split gets a null class loader.
+ null}; // The feature split with no dependency and no specified class loader.
if (addSplitDependencies) {
- ai.splitDependencies = new SparseArray<>(6 + 1);
+ ai.splitDependencies = new SparseArray<>(splitClassLoaderNames.length + 1);
ai.splitDependencies.put(0, new int[] {-1}); // base has no dependency
ai.splitDependencies.put(1, new int[] {2}); // split 1 depends on 2
ai.splitDependencies.put(2, new int[] {4}); // split 2 depends on 4
@@ -71,18 +97,24 @@ public class DexoptUtilsTest {
ai.splitDependencies.put(4, new int[] {0}); // split 4 depends on base
ai.splitDependencies.put(5, new int[] {0}); // split 5 depends on base
ai.splitDependencies.put(6, new int[] {5}); // split 6 depends on 5
+ // Do not add the config split to the dependency list.
+ // Do not add the feature split with no dependency to the dependency list.
}
}
- return ai;
+ TestData data = new TestData();
+ data.info = ai;
+ data.pathsWithCode = pathsWithCode;
+ return data;
}
@Test
public void testSplitChain() {
- ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
+ TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
- assertEquals(7, contexts.length);
+ assertEquals(9, contexts.length);
assertEquals("PCL[a.dex:b.dex]", contexts[0]);
assertEquals("PCL[];PCL[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]",
contexts[1]);
@@ -91,15 +123,18 @@ public class DexoptUtilsTest {
assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+ assertEquals(null, contexts[7]); // config split
+ assertEquals("PCL[]", contexts[8]); // feature split with no dependency
}
@Test
public void testSplitChainNoSplitDependencies() {
- ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false);
+ TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false);
String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
- assertEquals(7, contexts.length);
+ assertEquals(9, contexts.length);
assertEquals("PCL[a.dex:b.dex]", contexts[0]);
assertEquals("PCL[a.dex:b.dex:base.dex]", contexts[1]);
assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex]", contexts[2]);
@@ -111,15 +146,21 @@ public class DexoptUtilsTest {
assertEquals(
"PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]",
contexts[6]);
+ assertEquals(null, contexts[7]); // config split
+ assertEquals(
+ "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]",
+ contexts[8]); // feature split with no dependency
}
@Test
public void testSplitChainNoIsolationNoSharedLibrary() {
- ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
- ai.privateFlags = ai.privateFlags & (~ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING);
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null);
+ TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
+ data.info.privateFlags = data.info.privateFlags
+ & (~ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, null, data.pathsWithCode);
- assertEquals(7, contexts.length);
+ assertEquals(9, contexts.length);
assertEquals("PCL[]", contexts[0]);
assertEquals("PCL[base.dex]", contexts[1]);
assertEquals("PCL[base.dex:base-1.dex]", contexts[2]);
@@ -129,14 +170,20 @@ public class DexoptUtilsTest {
assertEquals(
"PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]",
contexts[6]);
+ assertEquals(null, contexts[7]); // config split
+ assertEquals(
+ "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]",
+ contexts[8]); // feature split with no dependency
}
+
@Test
public void testSplitChainNoSharedLibraries() {
- ApplicationInfo ai = createMockApplicationInfo(
+ TestData data = createMockApplicationInfo(
DELEGATE_LAST_CLASS_LOADER_NAME, true, true);
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, null, data.pathsWithCode);
- assertEquals(7, contexts.length);
+ assertEquals(9, contexts.length);
assertEquals("PCL[]", contexts[0]);
assertEquals("PCL[];PCL[base-2.dex];PCL[base-4.dex];PCL[base.dex]", contexts[1]);
assertEquals("PCL[];PCL[base-4.dex];PCL[base.dex]", contexts[2]);
@@ -144,16 +191,19 @@ public class DexoptUtilsTest {
assertEquals("PCL[];PCL[base.dex]", contexts[4]);
assertEquals("PCL[];PCL[base.dex]", contexts[5]);
assertEquals("PCL[];PCL[base-5.dex];PCL[base.dex]", contexts[6]);
+ assertEquals(null, contexts[7]); // config split
+ assertEquals("PCL[]", contexts[8]); // feature split with no dependency
}
@Test
public void testSplitChainWithNullPrimaryClassLoader() {
// A null classLoaderName should mean PathClassLoader.
- ApplicationInfo ai = createMockApplicationInfo(null, true, true);
+ TestData data = createMockApplicationInfo(null, true, true);
String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
- assertEquals(7, contexts.length);
+ assertEquals(9, contexts.length);
assertEquals("PCL[a.dex:b.dex]", contexts[0]);
assertEquals("PCL[];PCL[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]);
assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
@@ -161,13 +211,16 @@ public class DexoptUtilsTest {
assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+ assertEquals(null, contexts[7]); // config split
+ assertEquals("PCL[]", contexts[8]); // feature split with no dependency
}
@Test
public void tesNoSplits() {
- ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
+ TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
assertEquals(1, contexts.length);
assertEquals("PCL[a.dex:b.dex]", contexts[0]);
@@ -175,9 +228,10 @@ public class DexoptUtilsTest {
@Test
public void tesNoSplitsNullClassLoaderName() {
- ApplicationInfo ai = createMockApplicationInfo(null, false, false);
+ TestData data = createMockApplicationInfo(null, false, false);
String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
assertEquals(1, contexts.length);
assertEquals("PCL[a.dex:b.dex]", contexts[0]);
@@ -185,10 +239,11 @@ public class DexoptUtilsTest {
@Test
public void tesNoSplitDelegateLast() {
- ApplicationInfo ai = createMockApplicationInfo(
+ TestData data = createMockApplicationInfo(
DELEGATE_LAST_CLASS_LOADER_NAME, false, false);
String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
assertEquals(1, contexts.length);
assertEquals("PCL[a.dex:b.dex]", contexts[0]);
@@ -196,8 +251,9 @@ public class DexoptUtilsTest {
@Test
public void tesNoSplitsNoSharedLibraries() {
- ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null);
+ TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, null, data.pathsWithCode);
assertEquals(1, contexts.length);
assertEquals("PCL[]", contexts[0]);
@@ -205,15 +261,55 @@ public class DexoptUtilsTest {
@Test
public void tesNoSplitDelegateLastNoSharedLibraries() {
- ApplicationInfo ai = createMockApplicationInfo(
+ TestData data = createMockApplicationInfo(
DELEGATE_LAST_CLASS_LOADER_NAME, false, false);
- String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, null, data.pathsWithCode);
assertEquals(1, contexts.length);
assertEquals("PCL[]", contexts[0]);
}
@Test
+ public void testContextWithNoCode() {
+ TestData data = createMockApplicationInfo(null, true, false);
+ Arrays.fill(data.pathsWithCode, false);
+
+ String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
+
+ assertEquals(9, contexts.length);
+ assertEquals(null, contexts[0]);
+ assertEquals(null, contexts[1]);
+ assertEquals(null, contexts[2]);
+ assertEquals(null, contexts[3]);
+ assertEquals(null, contexts[4]);
+ assertEquals(null, contexts[5]);
+ assertEquals(null, contexts[6]);
+ assertEquals(null, contexts[7]);
+ }
+
+ @Test
+ public void testContextBaseNoCode() {
+ TestData data = createMockApplicationInfo(null, true, true);
+ data.pathsWithCode[0] = false;
+ String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
+
+ assertEquals(9, contexts.length);
+ assertEquals(null, contexts[0]);
+ assertEquals("PCL[];PCL[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]);
+ assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
+ assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]);
+ assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
+ assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
+ assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+ assertEquals(null, contexts[7]);
+ }
+
+ @Test
public void testProcessContextForDexLoad() {
List<String> classLoaders = Arrays.asList(
DELEGATE_LAST_CLASS_LOADER_NAME,
@@ -226,8 +322,8 @@ public class DexoptUtilsTest {
String[] context = DexoptUtils.processContextForDexLoad(classLoaders, classPaths);
assertNotNull(context);
assertEquals(2, context.length);
- assertEquals("DLC[];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[0]);
- assertEquals("DLC[foo.dex];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[1]);
+ assertEquals("PCL[];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[0]);
+ assertEquals("PCL[foo.dex];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[1]);
}
@Test
@@ -276,4 +372,49 @@ public class DexoptUtilsTest {
}
assertTrue(gotException);
}
+
+ @Test
+ public void testEncodeClassLoader() {
+ assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoader(
+ SKIP_SHARED_LIBRARY_CHECK, "dalvik.system.PathClassLoader"));
+ assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoader(
+ SKIP_SHARED_LIBRARY_CHECK, "dalvik.system.DexClassLoader"));
+ assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoader(
+ SKIP_SHARED_LIBRARY_CHECK, "dalvik.system.DelegateLastClassLoader"));
+ assertEquals("PCL[xyz]", DexoptUtils.encodeClassLoader("xyz",
+ "dalvik.system.PathClassLoader"));
+ assertEquals("PCL[xyz]", DexoptUtils.encodeClassLoader("xyz",
+ "dalvik.system.DexClassLoader"));
+ assertEquals("DLC[xyz]", DexoptUtils.encodeClassLoader("xyz",
+ "dalvik.system.DelegateLastClassLoader"));
+ assertEquals("PCL[xyz]", DexoptUtils.encodeClassLoader("xyz", null));
+ assertEquals("abc[xyz]", DexoptUtils.encodeClassLoader("xyz", "abc"));
+
+ try {
+ DexoptUtils.encodeClassLoader(null, "abc");
+ fail(); // Exception should be caught.
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void testEncodeClassLoaderChain() {
+ assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoaderChain(
+ SKIP_SHARED_LIBRARY_CHECK, "PCL[a]"));
+ assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoaderChain("PCL[a]",
+ SKIP_SHARED_LIBRARY_CHECK));
+ assertEquals("PCL[a];DLC[b]", DexoptUtils.encodeClassLoaderChain("PCL[a]",
+ "DLC[b]"));
+ assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoaderChain("PCL[a]",
+ SKIP_SHARED_LIBRARY_CHECK));
+
+ try {
+ DexoptUtils.encodeClassLoaderChain("a", null);
+ fail(); // exception is expected
+ } catch (NullPointerException expected) {}
+
+ try {
+ DexoptUtils.encodeClassLoaderChain(null, "b");
+ fail(); // exception is expected
+ } catch (NullPointerException expected) {}
+ }
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 221986395fd3..946d237f5ba6 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -902,7 +902,7 @@ public class UsbDeviceManager {
updateCurrentAccessory();
}
if (mBootCompleted) {
- if (!mConnected) {
+ if (!mConnected && !hasMessages(MSG_ACCESSORY_MODE_ENTER_TIMEOUT)) {
// restore defaults when USB is disconnected
setEnabledFunctions(null, !mAdbEnabled, false);
}
diff --git a/telephony/java/android/telephony/Telephony.java b/telephony/java/android/telephony/Telephony.java
index e64e815248e4..1b50861707ed 100644
--- a/telephony/java/android/telephony/Telephony.java
+++ b/telephony/java/android/telephony/Telephony.java
@@ -3250,4 +3250,69 @@ public final class Telephony {
*/
public static final String IS_USING_CARRIER_AGGREGATION = "is_using_carrier_aggregation";
}
+
+ /**
+ * Contains carrier identification information.
+ * @hide
+ */
+ public static final class CarrierIdentification implements BaseColumns {
+ /**
+ * Numeric operator ID (as String). {@code MCC + MNC}
+ * <P>Type: TEXT </P>
+ */
+ public static final String MCCMNC = "mccmnc";
+
+ /**
+ * Group id level 1 (as String).
+ * <P>Type: TEXT </P>
+ */
+ public static final String GID1 = "gid1";
+
+ /**
+ * Group id level 2 (as String).
+ * <P>Type: TEXT </P>
+ */
+ public static final String GID2 = "gid2";
+
+ /**
+ * Public Land Mobile Network name.
+ * <P>Type: TEXT </P>
+ */
+ public static final String PLMN = "plmn";
+
+ /**
+ * Prefix xpattern of IMSI (International Mobile Subscriber Identity).
+ * <P>Type: TEXT </P>
+ */
+ public static final String IMSI_PREFIX_XPATTERN = "imsi_prefix_xpattern";
+
+ /**
+ * Service Provider Name.
+ * <P>Type: TEXT </P>
+ */
+ public static final String SPN = "spn";
+
+ /**
+ * Prefer APN name.
+ * <P>Type: TEXT </P>
+ */
+ public static final String APN = "apn";
+
+ /**
+ * User facing carrier name.
+ * <P>Type: TEXT </P>
+ */
+ public static final String NAME = "carrier_name";
+
+ /**
+ * A unique carrier id
+ * <P>Type: INTEGER </P>
+ */
+ public static final String CID = "carrier_id";
+
+ /**
+ * The {@code content://} URI for this table.
+ */
+ public static final Uri CONTENT_URI = Uri.parse("content://carrier_identification");
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 450f485317ef..73f59379ff59 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2133,13 +2133,16 @@ public class TelephonyManager {
* @hide
*/
public String getSimOperatorNumeric() {
- int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+ int subId = mSubId;
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
- subId = SubscriptionManager.getDefaultSmsSubscriptionId();
+ subId = SubscriptionManager.getDefaultDataSubscriptionId();
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
- subId = SubscriptionManager.getDefaultVoiceSubscriptionId();
+ subId = SubscriptionManager.getDefaultSmsSubscriptionId();
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
- subId = SubscriptionManager.getDefaultSubscriptionId();
+ subId = SubscriptionManager.getDefaultVoiceSubscriptionId();
+ if (!SubscriptionManager.isUsableSubIdValue(subId)) {
+ subId = SubscriptionManager.getDefaultSubscriptionId();
+ }
}
}
}