Merge "Removed android.debug.JNITest from preloaded-classes"
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index cb7e1a0..7c25354 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -14,30 +14,11 @@
libandroid_runtime
LOCAL_MODULE:= app_process
-LOCAL_32_BIT_ONLY := true
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := app_process
+LOCAL_MODULE_STEM_64 := app_process64
include $(BUILD_EXECUTABLE)
-ifeq ($(TARGET_IS_64_BIT),true)
-
-# 64-bit app_process64
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- app_main.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libutils \
- liblog \
- libbinder \
- libandroid_runtime
-
-LOCAL_MODULE:= app_process64
-LOCAL_NO_2ND_ARCH := true
-include $(BUILD_EXECUTABLE)
-
-endif # TARGET_IS_64_BIT
-
# Build a variant of app_process binary linked with ASan runtime.
# ARM-only at the moment.
ifeq ($(TARGET_ARCH),arm)
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 8d2b739..3e8d6a0 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -10,8 +10,9 @@
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <utils/Log.h>
-#include <cutils/process_name.h>
#include <cutils/memory.h>
+#include <cutils/process_name.h>
+#include <cutils/properties.h>
#include <cutils/trace.h>
#include <android_runtime/AndroidRuntime.h>
@@ -30,31 +31,22 @@
class AppRuntime : public AndroidRuntime
{
public:
- AppRuntime()
- : mParentDir(NULL)
- , mClassName(NULL)
+ AppRuntime(char* argBlockStart, const size_t argBlockLength)
+ : AndroidRuntime(argBlockStart, argBlockLength)
, mClass(NULL)
- , mArgC(0)
- , mArgV(NULL)
{
}
-#if 0
- // this appears to be unused
- const char* getParentDir() const
- {
- return mParentDir;
- }
-#endif
-
- const char* getClassName() const
- {
- return mClassName;
+ void setClassNameAndArgs(const String8& className, int argc, char * const *argv) {
+ mClassName = className;
+ for (int i = 0; i < argc; ++i) {
+ mArgs.add(String8(argv[i]));
+ }
}
virtual void onVmCreated(JNIEnv* env)
{
- if (mClassName == NULL) {
+ if (mClassName.isEmpty()) {
return; // Zygote. Nothing to do here.
}
@@ -71,10 +63,10 @@
* executing boot class Java code and thereby deny ourselves access to
* non-boot classes.
*/
- char* slashClassName = toSlashClassName(mClassName);
+ char* slashClassName = toSlashClassName(mClassName.string());
mClass = env->FindClass(slashClassName);
if (mClass == NULL) {
- ALOGE("ERROR: could not find class '%s'\n", mClassName);
+ ALOGE("ERROR: could not find class '%s'\n", mClassName.string());
}
free(slashClassName);
@@ -88,7 +80,7 @@
proc->startThreadPool();
AndroidRuntime* ar = AndroidRuntime::getRuntime();
- ar->callMain(mClassName, mClass, mArgC, mArgV);
+ ar->callMain(mClassName, mClass, mArgs);
IPCThreadState::self()->stopProcess();
}
@@ -114,46 +106,70 @@
}
- const char* mParentDir;
- const char* mClassName;
+ String8 mClassName;
+ Vector<String8> mArgs;
jclass mClass;
- int mArgC;
- const char* const* mArgV;
};
}
using namespace android;
-/*
- * sets argv0 to as much of newArgv0 as will fit
- */
-static void setArgv0(const char *argv0, const char *newArgv0)
-{
- strlcpy(const_cast<char *>(argv0), newArgv0, strlen(argv0));
+static size_t computeArgBlockSize(int argc, char* const argv[]) {
+ // TODO: This assumes that all arguments are allocated in
+ // contiguous memory. There isn't any documented guarantee
+ // that this is the case, but this is how the kernel does it
+ // (see fs/exec.c).
+ //
+ // Also note that this is a constant for "normal" android apps.
+ // Since they're forked from zygote, the size of their command line
+ // is the size of the zygote command line.
+ //
+ // We change the process name of the process by over-writing
+ // the start of the argument block (argv[0]) with the new name of
+ // the process, so we'd mysteriously start getting truncated process
+ // names if the zygote command line decreases in size.
+ uintptr_t start = reinterpret_cast<uintptr_t>(argv[0]);
+ uintptr_t end = reinterpret_cast<uintptr_t>(argv[argc - 1]);
+ end += strlen(argv[argc - 1]);
+
+ return (end - start);
}
+#if defined(__LP64__)
+static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist64";
+#else
+static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist32";
+#endif
+
int main(int argc, char* const argv[])
{
- // These are global variables in ProcessState.cpp
- mArgC = argc;
- mArgV = argv;
-
- mArgLen = 0;
- for (int i=0; i<argc; i++) {
- mArgLen += strlen(argv[i]) + 1;
- }
- mArgLen--;
-
- AppRuntime runtime;
- const char* argv0 = argv[0];
-
+ AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// Process command line arguments
// ignore argv[0]
argc--;
argv++;
- // Everything up to '--' or first non '-' arg goes to the vm
+ // Everything up to '--' or first non '-' arg goes to the vm.
+ //
+ // The first argument after the VM args is the "parent dir", which
+ // is currently unused.
+ //
+ // After the parent dir, we expect one or more the following internal
+ // arguments :
+ //
+ // --zygote : Start in zygote mode
+ // --start-system-server : Start the system server.
+ // --application : Start in application (stand alone, non zygote) mode.
+ // --nice-name : The nice name for this process.
+ //
+ // For non zygote starts, these arguments will be followed by
+ // the main class name. All remaining arguments are passed to
+ // the main method of this class.
+ //
+ // For zygote starts, all remaining arguments are passed to the zygote.
+ // main function.
+
int i = runtime.addVmArguments(argc, argv);
@@ -161,14 +177,13 @@
bool zygote = false;
bool startSystemServer = false;
bool application = false;
- const char* parentDir = NULL;
const char* niceName = NULL;
- const char* className = NULL;
+ String8 className;
+
+ ++i; // Skip unused "parent dir" argument.
while (i < argc) {
const char* arg = argv[i++];
- if (!parentDir) {
- parentDir = arg;
- } else if (strcmp(arg, "--zygote") == 0) {
+ if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = "zygote";
} else if (strcmp(arg, "--start-system-server") == 0) {
@@ -178,28 +193,52 @@
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName = arg + 12;
} else {
- className = arg;
+ className.setTo(arg);
break;
}
}
+ Vector<String8> args;
+ if (!className.isEmpty()) {
+ // We're not in zygote mode, the only argument we need to pass
+ // to RuntimeInit is the application argument.
+ //
+ // The Remainder of args get passed to startup class main(). Make
+ // copies of them before we overwrite them with the process name.
+ args.add(application ? String8("application") : String8("tool"));
+ runtime.setClassNameAndArgs(className, argc - i, argv + i);
+ } else {
+ if (startSystemServer) {
+ args.add(String8("start-system-server"));
+ }
+
+ char prop[PROP_VALUE_MAX];
+ if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
+ LOG_ALWAYS_FATAL("app_process: Unable to deterimine ABI list from property %s.",
+ ABI_LIST_PROPERTY);
+ return 11;
+ }
+
+ String8 abiFlag("--abi-list=");
+ abiFlag.append(prop);
+ args.add(abiFlag);
+
+ // In zygote mode, pass all remaining arguments to the zygote
+ // main() method.
+ for (; i < argc; ++i) {
+ args.add(String8(argv[i]));
+ }
+ }
+
if (niceName && *niceName) {
- setArgv0(argv0, niceName);
+ runtime.setArgv0(niceName);
set_process_name(niceName);
}
- runtime.mParentDir = parentDir;
-
if (zygote) {
- runtime.start("com.android.internal.os.ZygoteInit",
- startSystemServer ? "start-system-server" : "");
+ runtime.start("com.android.internal.os.ZygoteInit", args);
} else if (className) {
- // Remainder of args get passed to startup class main()
- runtime.mClassName = className;
- runtime.mArgC = argc - i;
- runtime.mArgV = argv + i;
- runtime.start("com.android.internal.os.RuntimeInit",
- application ? "application" : "tool");
+ runtime.start("com.android.internal.os.RuntimeInit", args);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 9c46d96..1a1610d 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -441,6 +441,15 @@
public String nativeLibraryDir;
/**
+ * The ABI that this application requires, This is inferred from the ABIs
+ * of the native JNI libraries the application bundles. Will be {@code null}
+ * if this application does not require any particular ABI.
+ *
+ * {@hide}
+ */
+ public String requiredCpuAbi;
+
+ /**
* The kernel user-ID that has been assigned to this application;
* currently this is not a unique ID (multiple applications can have
* the same uid).
@@ -570,6 +579,7 @@
sourceDir = orig.sourceDir;
publicSourceDir = orig.publicSourceDir;
nativeLibraryDir = orig.nativeLibraryDir;
+ requiredCpuAbi = orig.requiredCpuAbi;
resourceDirs = orig.resourceDirs;
seinfo = orig.seinfo;
sharedLibraryFiles = orig.sharedLibraryFiles;
@@ -610,6 +620,7 @@
dest.writeString(sourceDir);
dest.writeString(publicSourceDir);
dest.writeString(nativeLibraryDir);
+ dest.writeString(requiredCpuAbi);
dest.writeStringArray(resourceDirs);
dest.writeString(seinfo);
dest.writeStringArray(sharedLibraryFiles);
@@ -649,6 +660,7 @@
sourceDir = source.readString();
publicSourceDir = source.readString();
nativeLibraryDir = source.readString();
+ requiredCpuAbi = source.readString();
resourceDirs = source.readStringArray();
seinfo = source.readString();
sharedLibraryFiles = source.readStringArray();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c97c2b8..8ce7e97 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -675,6 +675,25 @@
public static final int INSTALL_FAILED_USER_RESTRICTED = -111;
/**
+ * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
+ * if the system failed to install the package because its packaged native code did not
+ * match any of the ABIs supported by the system.
+ *
+ * @hide
+ */
+ public static final int INSTALL_FAILED_NO_MATCHING_ABIS = -112;
+
+ /**
+ * Internal return code for NativeLibraryHelper methods to indicate that the package
+ * being processed did not contain any native code. This is placed here only so that
+ * it can belong to the same value space as the other install failure codes.
+ *
+ * @hide
+ */
+ public static final int NO_NATIVE_LIBRARIES = -113;
+
+ /**
* Flag parameter for {@link #deletePackage} to indicate that you don't want to delete the
* package's data directory.
*
diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java
index 1e476fc..d06355d 100644
--- a/core/java/android/net/http/CertificateChainValidator.java
+++ b/core/java/android/net/http/CertificateChainValidator.java
@@ -16,22 +16,28 @@
package android.net.http;
-import com.android.org.conscrypt.SSLParametersImpl;
-import com.android.org.conscrypt.TrustManagerImpl;
+import android.util.Slog;
import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.security.GeneralSecurityException;
-import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
-import javax.net.ssl.DefaultHostnameVerifier;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
-import javax.net.ssl.X509TrustManager;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509ExtendedTrustManager;
/**
* Class responsible for all server certificate validation functionality
@@ -39,28 +45,54 @@
* {@hide}
*/
public class CertificateChainValidator {
+ private static final String TAG = "CertificateChainValidator";
- /**
- * The singleton instance of the certificate chain validator
- */
- private static final CertificateChainValidator sInstance
- = new CertificateChainValidator();
+ private static class NoPreloadHolder {
+ /**
+ * The singleton instance of the certificate chain validator.
+ */
+ private static final CertificateChainValidator sInstance = new CertificateChainValidator();
- private static final DefaultHostnameVerifier sVerifier
- = new DefaultHostnameVerifier();
+ /**
+ * The singleton instance of the hostname verifier.
+ */
+ private static final HostnameVerifier sVerifier = HttpsURLConnection
+ .getDefaultHostnameVerifier();
+ }
+
+ private X509ExtendedTrustManager mTrustManager;
/**
* @return The singleton instance of the certificates chain validator
*/
public static CertificateChainValidator getInstance() {
- return sInstance;
+ return NoPreloadHolder.sInstance;
}
/**
* Creates a new certificate chain validator. This is a private constructor.
* If you need a Certificate chain validator, call getInstance().
*/
- private CertificateChainValidator() {}
+ private CertificateChainValidator() {
+ try {
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("X.509");
+ tmf.init((KeyStore) null);
+ for (TrustManager tm : tmf.getTrustManagers()) {
+ if (tm instanceof X509ExtendedTrustManager) {
+ mTrustManager = (X509ExtendedTrustManager) tm;
+ }
+ }
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("X.509 TrustManagerFactory must be available", e);
+ } catch (KeyStoreException e) {
+ throw new RuntimeException("X.509 TrustManagerFactory cannot be initialized", e);
+ }
+
+ if (mTrustManager == null) {
+ throw new RuntimeException(
+ "None of the X.509 TrustManagers are X509ExtendedTrustManager");
+ }
+ }
/**
* Performs the handshake and server certificates validation
@@ -136,14 +168,31 @@
* Handles updates to credential storage.
*/
public static void handleTrustStorageUpdate() {
-
+ TrustManagerFactory tmf;
try {
- X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultX509TrustManager();
- if( x509TrustManager instanceof TrustManagerImpl ) {
- TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager;
- trustManager.handleTrustStorageUpdate();
+ tmf = TrustManagerFactory.getInstance("X.509");
+ tmf.init((KeyStore) null);
+ } catch (NoSuchAlgorithmException e) {
+ Slog.w(TAG, "Couldn't find default X.509 TrustManagerFactory");
+ return;
+ } catch (KeyStoreException e) {
+ Slog.w(TAG, "Couldn't initialize default X.509 TrustManagerFactory", e);
+ return;
+ }
+
+ TrustManager[] tms = tmf.getTrustManagers();
+ boolean sentUpdate = false;
+ for (TrustManager tm : tms) {
+ try {
+ Method updateMethod = tm.getClass().getDeclaredMethod("handleTrustStorageUpdate");
+ updateMethod.setAccessible(true);
+ updateMethod.invoke(tm);
+ sentUpdate = true;
+ } catch (Exception e) {
}
- } catch (KeyManagementException ignored) {
+ }
+ if (!sentUpdate) {
+ Slog.w(TAG, "Didn't find a TrustManager to handle CA list update");
}
}
@@ -166,7 +215,8 @@
boolean valid = domain != null
&& !domain.isEmpty()
- && sVerifier.verify(domain, currCertificate);
+ && NoPreloadHolder.sVerifier.verify(domain,
+ new DelegatingSSLSession.CertificateWrap(currCertificate));
if (!valid) {
if (HttpLog.LOGV) {
HttpLog.v("certificate not for this host: " + domain);
@@ -175,13 +225,8 @@
}
try {
- X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultX509TrustManager();
- if (x509TrustManager instanceof TrustManagerImpl) {
- TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager;
- trustManager.checkServerTrusted(chain, authType, domain);
- } else {
- x509TrustManager.checkServerTrusted(chain, authType);
- }
+ getInstance().getTrustManager().checkServerTrusted(chain, authType,
+ new DelegatingSocketWrapper(domain));
return null; // No errors.
} catch (GeneralSecurityException e) {
if (HttpLog.LOGV) {
@@ -192,6 +237,12 @@
}
}
+ /**
+ * Returns the platform default {@link X509ExtendedTrustManager}.
+ */
+ private X509ExtendedTrustManager getTrustManager() {
+ return mTrustManager;
+ }
private void closeSocketThrowException(
SSLSocket socket, String errorMessage, String defaultErrorMessage)
@@ -217,4 +268,4 @@
throw new SSLHandshakeException(errorMessage);
}
-}
+}
\ No newline at end of file
diff --git a/core/java/android/net/http/DelegatingSSLSession.java b/core/java/android/net/http/DelegatingSSLSession.java
new file mode 100644
index 0000000..ff75b24
--- /dev/null
+++ b/core/java/android/net/http/DelegatingSSLSession.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2014 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.net.http;
+
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509ExtendedTrustManager;
+
+/**
+ * This is used when only a {@code hostname} is available but usage of the new API
+ * {@link X509ExtendedTrustManager#checkServerTrusted(X509Certificate[], String, Socket)}
+ * requires a {@link SSLSocket}.
+ *
+ * @hide
+ */
+public class DelegatingSSLSession implements SSLSession {
+ protected DelegatingSSLSession() {
+ }
+
+ public static class HostnameWrap extends DelegatingSSLSession {
+ private final String mHostname;
+
+ public HostnameWrap(String hostname) {
+ mHostname = hostname;
+ }
+
+ @Override
+ public String getPeerHost() {
+ return mHostname;
+ }
+ }
+
+ public static class CertificateWrap extends DelegatingSSLSession {
+ private final Certificate mCertificate;
+
+ public CertificateWrap(Certificate certificate) {
+ mCertificate = certificate;
+ }
+
+ @Override
+ public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+ return new Certificate[] { mCertificate };
+ }
+ }
+
+
+ @Override
+ public int getApplicationBufferSize() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getCipherSuite() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getCreationTime() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public byte[] getId() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getLastAccessedTime() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Certificate[] getLocalCertificates() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Principal getLocalPrincipal() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getPacketBufferSize() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public javax.security.cert.X509Certificate[] getPeerCertificateChain()
+ throws SSLPeerUnverifiedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getPeerHost() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getPeerPort() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getProtocol() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SSLSessionContext getSessionContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object getValue(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String[] getValueNames() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void invalidate() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isValid() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void putValue(String name, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeValue(String name) {
+ throw new UnsupportedOperationException();
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/net/http/DelegatingSocketWrapper.java b/core/java/android/net/http/DelegatingSocketWrapper.java
new file mode 100644
index 0000000..230d017
--- /dev/null
+++ b/core/java/android/net/http/DelegatingSocketWrapper.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2014 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.net.http;
+
+import java.io.IOException;
+
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509ExtendedTrustManager;
+
+/**
+ * This is used when only a {@code hostname} is available for
+ * {@link X509ExtendedTrustManager#checkServerTrusted(java.security.cert.X509Certificate[], String, Socket)}
+ * but we want to use the new API that requires a {@link SSLSocket}.
+ */
+class DelegatingSocketWrapper extends SSLSocket {
+ private String hostname;
+
+ public DelegatingSocketWrapper(String hostname) {
+ this.hostname = hostname;
+ }
+
+ @Override
+ public String[] getSupportedCipherSuites() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String[] getEnabledCipherSuites() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setEnabledCipherSuites(String[] suites) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String[] getSupportedProtocols() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String[] getEnabledProtocols() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setEnabledProtocols(String[] protocols) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SSLSession getSession() {
+ return new DelegatingSSLSession.HostnameWrap(hostname);
+ }
+
+ @Override
+ public void addHandshakeCompletedListener(HandshakeCompletedListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void startHandshake() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setUseClientMode(boolean mode) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean getUseClientMode() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setNeedClientAuth(boolean need) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setWantClientAuth(boolean want) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean getNeedClientAuth() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean getWantClientAuth() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setEnableSessionCreation(boolean flag) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean getEnableSessionCreation() {
+ throw new UnsupportedOperationException();
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java
index cfe5f27..d730a7b 100644
--- a/core/java/android/net/http/X509TrustManagerExtensions.java
+++ b/core/java/android/net/http/X509TrustManagerExtensions.java
@@ -22,14 +22,25 @@
import java.security.cert.X509Certificate;
import java.util.List;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
/**
* X509TrustManager wrapper exposing Android-added features.
- *
- * <p> The checkServerTrusted method allows callers to perform additional
- * verification of certificate chains after they have been successfully
- * verified by the platform.</p>
+ * <p>
+ * The checkServerTrusted method allows callers to perform additional
+ * verification of certificate chains after they have been successfully verified
+ * by the platform.
+ * </p>
+ * <p>
+ * If the returned certificate list is not needed, see also
+ * {@code X509ExtendedTrustManager#checkServerTrusted(X509Certificate[], String, java.net.Socket)}
+ * where an {@link SSLSocket} can be used to verify the given hostname during
+ * handshake using
+ * {@code SSLParameters#setEndpointIdentificationAlgorithm(String)}.
+ * </p>
*/
public class X509TrustManagerExtensions {
@@ -45,7 +56,8 @@
if (tm instanceof TrustManagerImpl) {
mDelegate = (TrustManagerImpl) tm;
} else {
- throw new IllegalArgumentException("tm is not a supported type of X509TrustManager");
+ throw new IllegalArgumentException("tm is an instance of " + tm.getClass().getName() +
+ " which is not a supported type of X509TrustManager");
}
}
@@ -61,6 +73,7 @@
*/
public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType,
String host) throws CertificateException {
- return mDelegate.checkServerTrusted(chain, authType, host);
+ return mDelegate.checkServerTrusted(chain, authType,
+ new DelegatingSSLSession.HostnameWrap(host));
}
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index bc51a60..c3313c5 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -74,7 +74,14 @@
/** A hardware serial number, if available. Alphanumeric only, case-insensitive. */
public static final String SERIAL = getString("ro.serialno");
-
+
+ /**
+ * A list of ABIs (in priority) order supported by this device.
+ *
+ * @hide
+ */
+ public static final String[] SUPPORTED_ABIS = getString("ro.product.cpu.abilist").split(",");
+
/** Various version strings. */
public static class VERSION {
/**
diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java
index ee7a4c6..6cec55a 100644
--- a/core/java/android/os/MemoryFile.java
+++ b/core/java/android/os/MemoryFile.java
@@ -63,12 +63,17 @@
* Allocates a new ashmem region. The region is initially not purgable.
*
* @param name optional name for the file (can be null).
- * @param length of the memory file in bytes.
+ * @param length of the memory file in bytes, must be non-negative.
* @throws IOException if the memory file could not be created.
*/
public MemoryFile(String name, int length) throws IOException {
mLength = length;
- mFD = native_open(name, length);
+ if (length >= 0) {
+ mFD = native_open(name, length);
+ } else {
+ throw new IOException("Invalid length: " + length);
+ }
+
if (length > 0) {
mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
} else {
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 631edd6..10ff27e 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -16,17 +16,18 @@
package android.os;
-import android.net.LocalSocketAddress;
import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
import android.util.Log;
-import dalvik.system.Zygote;
-
+import com.android.internal.os.Zygote;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-
+import java.util.Arrays;
+import java.util.List;
import libcore.io.Libcore;
/*package*/ class ZygoteStartFailedEx extends Exception {
@@ -47,17 +48,7 @@
private static final String ZYGOTE_SOCKET = "zygote";
- /**
- * Name of a process for running the platform's media services.
- * {@hide}
- */
- public static final String ANDROID_SHARED_MEDIA = "com.android.process.media";
-
- /**
- * Name of the process that Google content providers can share.
- * {@hide}
- */
- public static final String GOOGLE_SHARED_APP_CONTENT = "com.google.process.content";
+ private static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary";
/**
* Defines the UID/GID under which system code runs.
@@ -344,15 +335,112 @@
public static final int SIGNAL_QUIT = 3;
public static final int SIGNAL_KILL = 9;
public static final int SIGNAL_USR1 = 10;
-
- // State for communicating with zygote process
- static LocalSocket sZygoteSocket;
- static DataInputStream sZygoteInputStream;
- static BufferedWriter sZygoteWriter;
+ /**
+ * State for communicating with the zygote process.
+ */
+ static class ZygoteState {
+ final LocalSocket socket;
+ final DataInputStream inputStream;
+ final BufferedWriter writer;
+ final List<String> abiList;
- /** true if previous zygote open failed */
- static boolean sPreviousZygoteOpenFailed;
+ boolean mClosed;
+
+ private ZygoteState(LocalSocket socket, DataInputStream inputStream,
+ BufferedWriter writer, List<String> abiList) {
+ this.socket = socket;
+ this.inputStream = inputStream;
+ this.writer = writer;
+ this.abiList = abiList;
+ }
+
+ static ZygoteState connect(String socketAddress, int tries) throws ZygoteStartFailedEx {
+ LocalSocket zygoteSocket = null;
+ DataInputStream zygoteInputStream = null;
+ BufferedWriter zygoteWriter = null;
+
+ /*
+ * See bug #811181: Sometimes runtime can make it up before zygote.
+ * Really, we'd like to do something better to avoid this condition,
+ * but for now just wait a bit...
+ *
+ * TODO: This bug was filed in 2007. Get rid of this code. The zygote
+ * forks the system_server so it shouldn't be possible for the zygote
+ * socket to be brought up after the system_server is.
+ */
+ for (int i = 0; i < tries; i++) {
+ if (i > 0) {
+ try {
+ Log.i("Zygote", "Zygote not up yet, sleeping...");
+ Thread.sleep(ZYGOTE_RETRY_MILLIS);
+ } catch (InterruptedException ex) {
+ throw new ZygoteStartFailedEx(ex);
+ }
+ }
+
+ try {
+ zygoteSocket = new LocalSocket();
+ zygoteSocket.connect(new LocalSocketAddress(socketAddress,
+ LocalSocketAddress.Namespace.RESERVED));
+
+ zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
+
+ zygoteWriter = new BufferedWriter(new OutputStreamWriter(
+ zygoteSocket.getOutputStream()), 256);
+ break;
+ } catch (IOException ex) {
+ if (zygoteSocket != null) {
+ try {
+ zygoteSocket.close();
+ } catch (IOException ex2) {
+ Log.e(LOG_TAG,"I/O exception on close after exception", ex2);
+ }
+ }
+
+ zygoteSocket = null;
+ }
+ }
+
+ if (zygoteSocket == null) {
+ throw new ZygoteStartFailedEx("connect failed");
+ }
+
+ String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
+ Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString);
+
+ return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
+ Arrays.asList(abiListString.split(",")));
+ }
+
+ boolean matches(String abi) {
+ return abiList.contains(abi);
+ }
+
+ void close() {
+ try {
+ socket.close();
+ } catch (IOException ex) {
+ Log.e(LOG_TAG,"I/O exception on routine close", ex);
+ }
+
+ mClosed = true;
+ }
+
+ boolean isClosed() {
+ return mClosed;
+ }
+ }
+
+ /**
+ * The state of the connection to the primary zygote.
+ */
+ static ZygoteState primaryZygoteState;
+
+ /**
+ * The state of the connection to the secondary zygote.
+ */
+ static ZygoteState secondaryZygoteState;
/**
* Start a new process.
@@ -378,6 +466,7 @@
* @param debugFlags Additional flags.
* @param targetSdkVersion The target SDK version for the app.
* @param seInfo null-ok SELinux information for the new process.
+ * @param abi non-null the ABI this app should be started with.
* @param zygoteArgs Additional arguments to supply to the zygote process.
*
* @return An object that describes the result of the attempt to start the process.
@@ -391,10 +480,12 @@
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
+ String abi,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
- debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs);
+ debugFlags, mountExternal, targetSdkVersion, seInfo,
+ abi, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -407,78 +498,31 @@
static final int ZYGOTE_RETRY_MILLIS = 500;
/**
- * Tries to open socket to Zygote process if not already open. If
- * already open, does nothing. May block and retry.
+ * Queries the zygote for the list of ABIS it supports.
+ *
+ * @throws ZygoteStartFailedEx if the query failed.
*/
- private static void openZygoteSocketIfNeeded()
+ private static String getAbiList(BufferedWriter writer, DataInputStream inputStream)
throws ZygoteStartFailedEx {
+ try {
- int retryCount;
+ // Each query starts with the argument count (1 in this case)
+ writer.write("1");
+ // ... followed by a new-line.
+ writer.newLine();
+ // ... followed by our only argument.
+ writer.write("--query-abi-list");
+ writer.newLine();
+ writer.flush();
- if (sPreviousZygoteOpenFailed) {
- /*
- * If we've failed before, expect that we'll fail again and
- * don't pause for retries.
- */
- retryCount = 0;
- } else {
- retryCount = 10;
- }
+ // The response is a length prefixed stream of ASCII bytes.
+ int numBytes = inputStream.readInt();
+ byte[] bytes = new byte[numBytes];
+ inputStream.readFully(bytes);
- /*
- * See bug #811181: Sometimes runtime can make it up before zygote.
- * Really, we'd like to do something better to avoid this condition,
- * but for now just wait a bit...
- */
- for (int retry = 0
- ; (sZygoteSocket == null) && (retry < (retryCount + 1))
- ; retry++ ) {
-
- if (retry > 0) {
- try {
- Log.i("Zygote", "Zygote not up yet, sleeping...");
- Thread.sleep(ZYGOTE_RETRY_MILLIS);
- } catch (InterruptedException ex) {
- // should never happen
- }
- }
-
- try {
- sZygoteSocket = new LocalSocket();
-
- sZygoteSocket.connect(new LocalSocketAddress(ZYGOTE_SOCKET,
- LocalSocketAddress.Namespace.RESERVED));
-
- sZygoteInputStream
- = new DataInputStream(sZygoteSocket.getInputStream());
-
- sZygoteWriter =
- new BufferedWriter(
- new OutputStreamWriter(
- sZygoteSocket.getOutputStream()),
- 256);
-
- Log.i("Zygote", "Process: zygote socket opened");
-
- sPreviousZygoteOpenFailed = false;
- break;
- } catch (IOException ex) {
- if (sZygoteSocket != null) {
- try {
- sZygoteSocket.close();
- } catch (IOException ex2) {
- Log.e(LOG_TAG,"I/O exception on close after exception",
- ex2);
- }
- }
-
- sZygoteSocket = null;
- }
- }
-
- if (sZygoteSocket == null) {
- sPreviousZygoteOpenFailed = true;
- throw new ZygoteStartFailedEx("connect failed");
+ return new String(bytes, StandardCharsets.US_ASCII);
+ } catch (IOException ioe) {
+ throw new ZygoteStartFailedEx(ioe);
}
}
@@ -486,14 +530,12 @@
* Sends an argument list to the zygote process, which starts a new child
* and returns the child's pid. Please note: the present implementation
* replaces newlines in the argument list with spaces.
- * @param args argument list
- * @return An object that describes the result of the attempt to start the process.
+ *
* @throws ZygoteStartFailedEx if process start failed for any reason
*/
- private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList<String> args)
+ private static ProcessStartResult zygoteSendArgsAndGetResult(
+ ZygoteState zygoteState, ArrayList<String> args)
throws ZygoteStartFailedEx {
- openZygoteSocketIfNeeded();
-
try {
/**
* See com.android.internal.os.ZygoteInit.readArgumentList()
@@ -505,9 +547,11 @@
* the child or -1 on failure, followed by boolean to
* indicate whether a wrapper process was used.
*/
+ final BufferedWriter writer = zygoteState.writer;
+ final DataInputStream inputStream = zygoteState.inputStream;
- sZygoteWriter.write(Integer.toString(args.size()));
- sZygoteWriter.newLine();
+ writer.write(Integer.toString(args.size()));
+ writer.newLine();
int sz = args.size();
for (int i = 0; i < sz; i++) {
@@ -516,32 +560,22 @@
throw new ZygoteStartFailedEx(
"embedded newlines not allowed");
}
- sZygoteWriter.write(arg);
- sZygoteWriter.newLine();
+ writer.write(arg);
+ writer.newLine();
}
- sZygoteWriter.flush();
+ writer.flush();
// Should there be a timeout on this?
ProcessStartResult result = new ProcessStartResult();
- result.pid = sZygoteInputStream.readInt();
+ result.pid = inputStream.readInt();
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
- result.usingWrapper = sZygoteInputStream.readBoolean();
+ result.usingWrapper = inputStream.readBoolean();
return result;
} catch (IOException ex) {
- try {
- if (sZygoteSocket != null) {
- sZygoteSocket.close();
- }
- } catch (IOException ex2) {
- // we're going to fail anyway
- Log.e(LOG_TAG,"I/O exception on routine close", ex2);
- }
-
- sZygoteSocket = null;
-
+ zygoteState.close();
throw new ZygoteStartFailedEx(ex);
}
}
@@ -558,6 +592,7 @@
* @param debugFlags Additional flags.
* @param targetSdkVersion The target SDK version for the app.
* @param seInfo null-ok SELinux information for the new process.
+ * @param abi the ABI the process should use.
* @param extraArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
* @throws ZygoteStartFailedEx if process start failed for any reason
@@ -569,6 +604,7 @@
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
+ String abi,
String[] extraArgs)
throws ZygoteStartFailedEx {
synchronized(Process.class) {
@@ -636,10 +672,54 @@
}
}
- return zygoteSendArgsAndGetResult(argsForZygote);
+ return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
}
-
+
+ /**
+ * Returns the number of times we attempt a connection to the zygote. We
+ * sleep for {@link #ZYGOTE_RETRY_MILLIS} milliseconds between each try.
+ *
+ * This could probably be removed, see TODO in {@code ZygoteState#connect}.
+ */
+ private static int getNumTries(ZygoteState state) {
+ // Retry 10 times for the first connection to each zygote.
+ if (state == null) {
+ return 11;
+ }
+
+ // This means the connection has already been established, but subsequently
+ // closed, possibly due to an IOException. We retry just once if that's the
+ // case.
+ return 1;
+ }
+
+ /**
+ * Tries to open socket to Zygote process if not already open. If
+ * already open, does nothing. May block and retry.
+ */
+ private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
+ if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
+ primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET, getNumTries(primaryZygoteState));
+ }
+
+ if (primaryZygoteState.matches(abi)) {
+ return primaryZygoteState;
+ }
+
+ // The primary zygote didn't match. Try the secondary.
+ if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
+ secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET,
+ getNumTries(secondaryZygoteState));
+ }
+
+ if (secondaryZygoteState.matches(abi)) {
+ return secondaryZygoteState;
+ }
+
+ throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
+ }
+
/**
* Returns elapsed milliseconds of the time this process has run.
* @return Returns the number of milliseconds this process has return.
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index aa43bad..cd905fa 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -591,6 +591,7 @@
Object[] args = mConstructorArgs;
args[1] = attrs;
+ constructor.setAccessible(true);
final View view = constructor.newInstance(args);
if (view instanceof ViewStub) {
// always use ourselves when inflating ViewStub later
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index bc0d7e3..3fd2aa9 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -660,7 +660,7 @@
mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(MSG_FLUSH_LAYER_UPDATES));
}
- public boolean attachFunctor(int functor) {
+ public boolean attachFunctor(long functor) {
//noinspection SimplifiableIfStatement
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
return mAttachInfo.mHardwareRenderer.attachFunctor(mAttachInfo, functor);
@@ -668,7 +668,7 @@
return false;
}
- public void detachFunctor(int functor) {
+ public void detachFunctor(long functor) {
if (mAttachInfo.mHardwareRenderer != null) {
mAttachInfo.mHardwareRenderer.detachFunctor(functor);
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 7a9809f..3f35875 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5147,12 +5147,12 @@
final int width = mRight - mLeft;
final int padding = getCompoundPaddingLeft() + getCompoundPaddingRight();
final float dx = mLayout.getLineRight(0) - (width - padding);
- canvas.translate(isLayoutRtl ? -dx : +dx, 0.0f);
+ canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
}
if (mMarquee != null && mMarquee.isRunning()) {
final float dx = -mMarquee.getScroll();
- canvas.translate(isLayoutRtl ? -dx : +dx, 0.0f);
+ canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
}
}
@@ -5166,8 +5166,8 @@
}
if (mMarquee != null && mMarquee.shouldDrawGhost()) {
- final int dx = (int) mMarquee.getGhostOffset();
- canvas.translate(isLayoutRtl ? -dx : dx, 0.0f);
+ final float dx = mMarquee.getGhostOffset();
+ canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 70f90d3..1eda373 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -27,8 +27,9 @@
Intent intent = getIntent();
Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT);
if (!(targetParcelable instanceof Intent)) {
- Log.w("ChooseActivity", "Target is not an intent: " + targetParcelable);
+ Log.w("ChooserActivity", "Target is not an intent: " + targetParcelable);
finish();
+ super.onCreate(null);
return;
}
Intent target = (Intent)targetParcelable;
@@ -42,9 +43,10 @@
initialIntents = new Intent[pa.length];
for (int i=0; i<pa.length; i++) {
if (!(pa[i] instanceof Intent)) {
- Log.w("ChooseActivity", "Initial intent #" + i
+ Log.w("ChooserActivity", "Initial intent #" + i
+ " not an Intent: " + pa[i]);
finish();
+ super.onCreate(null);
return;
}
initialIntents[i] = (Intent)pa[i];
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 6d65782..ba419f9 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -16,7 +16,7 @@
package com.android.internal.content;
-import android.os.Build;
+import android.content.pm.PackageManager;
import android.util.Slog;
import java.io.File;
@@ -31,38 +31,76 @@
private static final boolean DEBUG_NATIVE = false;
- private static native long nativeSumNativeBinaries(String file, String cpuAbi, String cpuAbi2);
-
/**
- * Sums the size of native binaries in an APK.
+ * A handle to an opened APK. Used as input to the various NativeLibraryHelper
+ * methods. Allows us to scan and parse the APK exactly once instead of doing
+ * it multiple times.
*
- * @param apkFile APK file to scan for native libraries
- * @return size of all native binary files in bytes
+ * @hide
*/
- public static long sumNativeBinariesLI(File apkFile) {
- final String cpuAbi = Build.CPU_ABI;
- final String cpuAbi2 = Build.CPU_ABI2;
- return nativeSumNativeBinaries(apkFile.getPath(), cpuAbi, cpuAbi2);
+ public static class ApkHandle {
+ final String apkPath;
+ final long apkHandle;
+
+ public ApkHandle(String path) {
+ apkPath = path;
+ apkHandle = nativeOpenApk(apkPath);
+ }
+
+ public ApkHandle(File apkFile) {
+ apkPath = apkFile.getPath();
+ apkHandle = nativeOpenApk(apkPath);
+ }
+
+ public void close() {
+ nativeClose(apkHandle);
+ }
}
- private native static int nativeCopyNativeBinaries(String filePath, String sharedLibraryPath,
- String cpuAbi, String cpuAbi2);
+
+ private static native long nativeOpenApk(String path);
+ private static native void nativeClose(long handle);
+
+ private static native long nativeSumNativeBinaries(long handle, String cpuAbi);
+
+ /**
+ * Sums the size of native binaries in an APK for a given ABI.
+ *
+ * @return size of all native binary files in bytes
+ */
+ public static long sumNativeBinariesLI(ApkHandle handle, String abi) {
+ return nativeSumNativeBinaries(handle.apkHandle, abi);
+ }
+
+ private native static int nativeCopyNativeBinaries(long handle,
+ String sharedLibraryPath, String abiToCopy);
/**
* Copies native binaries to a shared library directory.
*
- * @param apkFile APK file to scan for native libraries
+ * @param handle APK file to scan for native libraries
* @param sharedLibraryDir directory for libraries to be copied to
* @return {@link PackageManager#INSTALL_SUCCEEDED} if successful or another
* error code from that class if not
*/
- public static int copyNativeBinariesIfNeededLI(File apkFile, File sharedLibraryDir) {
- final String cpuAbi = Build.CPU_ABI;
- final String cpuAbi2 = Build.CPU_ABI2;
- return nativeCopyNativeBinaries(apkFile.getPath(), sharedLibraryDir.getPath(), cpuAbi,
- cpuAbi2);
+ public static int copyNativeBinariesIfNeededLI(ApkHandle handle, File sharedLibraryDir,
+ String abi) {
+ return nativeCopyNativeBinaries(handle.apkHandle, sharedLibraryDir.getPath(), abi);
}
+ /**
+ * Checks if a given APK contains native code for any of the provided
+ * {@code supportedAbis}. Returns an index into {@code supportedAbis} if a matching
+ * ABI is found, {@link PackageManager#NO_NATIVE_LIBRARIES} if the
+ * APK doesn't contain any native code, and
+ * {@link PackageManager#INSTALL_FAILED_NO_MATCHING_ABIS} if none of the ABIs match.
+ */
+ public static int findSupportedAbi(ApkHandle handle, String[] supportedAbis) {
+ return nativeFindSupportedAbi(handle.apkHandle, supportedAbis);
+ }
+
+ private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis);
+
// Convenience method to call removeNativeBinariesFromDirLI(File)
public static boolean removeNativeBinariesLI(String nativeLibraryPath) {
return removeNativeBinariesFromDirLI(new File(nativeLibraryPath));
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index c6b3e7c..3301cbe 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -25,9 +25,6 @@
import java.io.IOException;
import libcore.io.IoUtils;
-import libcore.io.Libcore;
-
-import dalvik.system.Zygote;
/**
* Startup class for the wrapper process.
@@ -95,7 +92,7 @@
* @param niceName The nice name for the application, or null if none.
* @param targetSdkVersion The target SDK version for the app.
* @param pipeFd The pipe to which the application's pid should be written, or null if none.
- * @param args Arguments for {@link RuntimeInit.main}.
+ * @param args Arguments for {@link RuntimeInit#main}.
*/
public static void execApplication(String invokeWith, String niceName,
int targetSdkVersion, FileDescriptor pipeFd, String[] args) {
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
new file mode 100644
index 0000000..c5fa0a1
--- /dev/null
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+
+import dalvik.system.ZygoteHooks;
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+
+/** @hide */
+public final class Zygote {
+ /*
+ * Bit values for "debugFlags" argument. The definitions are duplicated
+ * in the native code.
+ */
+
+ /** enable debugging over JDWP */
+ public static final int DEBUG_ENABLE_DEBUGGER = 1;
+ /** enable JNI checks */
+ public static final int DEBUG_ENABLE_CHECKJNI = 1 << 1;
+ /** enable Java programming language "assert" statements */
+ public static final int DEBUG_ENABLE_ASSERT = 1 << 2;
+ /** disable the JIT compiler */
+ public static final int DEBUG_ENABLE_SAFEMODE = 1 << 3;
+ /** Enable logging of third-party JNI activity. */
+ public static final int DEBUG_ENABLE_JNI_LOGGING = 1 << 4;
+
+ /** No external storage should be mounted. */
+ public static final int MOUNT_EXTERNAL_NONE = 0;
+ /** Single-user external storage should be mounted. */
+ public static final int MOUNT_EXTERNAL_SINGLEUSER = 1;
+ /** Multi-user external storage should be mounted. */
+ public static final int MOUNT_EXTERNAL_MULTIUSER = 2;
+ /** All multi-user external storage should be mounted. */
+ public static final int MOUNT_EXTERNAL_MULTIUSER_ALL = 3;
+
+ private static final ZygoteHooks VM_HOOKS = new ZygoteHooks();
+
+ private Zygote() {}
+
+ /**
+ * Forks a new VM instance. The current VM must have been started
+ * with the -Xzygote flag. <b>NOTE: new instance keeps all
+ * root capabilities. The new process is expected to call capset()</b>.
+ *
+ * @param uid the UNIX uid that the new process should setuid() to after
+ * fork()ing and and before spawning any threads.
+ * @param gid the UNIX gid that the new process should setgid() to after
+ * fork()ing and and before spawning any threads.
+ * @param gids null-ok; a list of UNIX gids that the new process should
+ * setgroups() to after fork and before spawning any threads.
+ * @param debugFlags bit flags that enable debugging features.
+ * @param rlimits null-ok an array of rlimit tuples, with the second
+ * dimension having a length of 3 and representing
+ * (resource, rlim_cur, rlim_max). These are set via the posix
+ * setrlimit(2) call.
+ * @param seInfo null-ok a string specifying SELinux information for
+ * the new process.
+ * @param niceName null-ok a string specifying the process name.
+ * @param fdsToClose an array of ints, holding one or more POSIX
+ * file descriptor numbers that are to be closed by the child
+ * (and replaced by /dev/null) after forking. An integer value
+ * of -1 in any entry in the array means "ignore this one".
+ *
+ * @return 0 if this is the child, pid of the child
+ * if this is the parent, or -1 on error.
+ */
+ public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
+ int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose) {
+ VM_HOOKS.preFork();
+ int pid = nativeForkAndSpecialize(
+ uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose);
+ VM_HOOKS.postForkCommon();
+ return pid;
+ }
+
+ native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int debugFlags,
+ int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose);
+
+ /**
+ * Special method to start the system server process. In addition to the
+ * common actions performed in forkAndSpecialize, the pid of the child
+ * process is recorded such that the death of the child process will cause
+ * zygote to exit.
+ *
+ * @param uid the UNIX uid that the new process should setuid() to after
+ * fork()ing and and before spawning any threads.
+ * @param gid the UNIX gid that the new process should setgid() to after
+ * fork()ing and and before spawning any threads.
+ * @param gids null-ok; a list of UNIX gids that the new process should
+ * setgroups() to after fork and before spawning any threads.
+ * @param debugFlags bit flags that enable debugging features.
+ * @param rlimits null-ok an array of rlimit tuples, with the second
+ * dimension having a length of 3 and representing
+ * (resource, rlim_cur, rlim_max). These are set via the posix
+ * setrlimit(2) call.
+ * @param permittedCapabilities argument for setcap()
+ * @param effectiveCapabilities argument for setcap()
+ *
+ * @return 0 if this is the child, pid of the child
+ * if this is the parent, or -1 on error.
+ */
+ public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags,
+ int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
+ VM_HOOKS.preFork();
+ int pid = nativeForkSystemServer(
+ uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities);
+ VM_HOOKS.postForkCommon();
+ return pid;
+ }
+
+ native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,
+ int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
+
+ private static void callPostForkChildHooks(int debugFlags) {
+ VM_HOOKS.postForkChild(debugFlags);
+ }
+
+
+ /**
+ * Executes "/system/bin/sh -c <command>" using the exec() system call.
+ * This method throws a runtime exception if exec() failed, otherwise, this
+ * method never returns.
+ *
+ * @param command The shell command to execute.
+ */
+ public static void execShell(String command) {
+ String[] args = { "/system/bin/sh", "-c", command };
+ try {
+ Libcore.os.execv(args[0], args);
+ } catch (ErrnoException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Appends quotes shell arguments to the specified string builder.
+ * The arguments are quoted using single-quotes, escaped if necessary,
+ * prefixed with a space, and appended to the command.
+ *
+ * @param command A string builder for the shell command being constructed.
+ * @param args An array of argument strings to be quoted and appended to the command.
+ * @see #execShell(String)
+ */
+ public static void appendQuotedShellArgs(StringBuilder command, String[] args) {
+ for (String arg : args) {
+ command.append(" '").append(arg.replace("'", "'\\''")).append("'");
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index f9a1f89..58a8e62 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -22,10 +22,7 @@
import android.os.SELinux;
import android.os.SystemProperties;
import android.util.Log;
-
import dalvik.system.PathClassLoader;
-import dalvik.system.Zygote;
-
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -35,8 +32,8 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-
import libcore.io.ErrnoException;
import libcore.io.IoUtils;
import libcore.io.Libcore;
@@ -60,7 +57,7 @@
private static final int CONNECTION_TIMEOUT_MILLIS = 1000;
/** max number of arguments that a connection can specify */
- private static final int MAX_ZYGOTE_ARGC=1024;
+ private static final int MAX_ZYGOTE_ARGC = 1024;
/**
* The command socket.
@@ -74,15 +71,18 @@
private final BufferedReader mSocketReader;
private final Credentials peer;
private final String peerSecurityContext;
+ private final String abiList;
/**
* Constructs instance from connected socket.
*
* @param socket non-null; connected socket
+ * @param abiList non-null; a list of ABIs this zygote supports.
* @throws IOException
*/
- ZygoteConnection(LocalSocket socket) throws IOException {
+ ZygoteConnection(LocalSocket socket, String abiList) throws IOException {
mSocket = socket;
+ this.abiList = abiList;
mSocketOutStream
= new DataOutputStream(socket.getOutputStream());
@@ -112,43 +112,6 @@
}
/**
- * Reads start commands from an open command socket.
- * Start commands are presently a pair of newline-delimited lines
- * indicating a) class to invoke main() on b) nice name to set argv[0] to.
- * Continues to read commands and forkAndSpecialize children until
- * the socket is closed. This method is used in ZYGOTE_FORK_MODE
- *
- * @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main()
- * method in child process
- */
- void run() throws ZygoteInit.MethodAndArgsCaller {
-
- int loopCount = ZygoteInit.GC_LOOP_COUNT;
-
- while (true) {
- /*
- * Call gc() before we block in readArgumentList().
- * It's work that has to be done anyway, and it's better
- * to avoid making every child do it. It will also
- * madvise() any free memory as a side-effect.
- *
- * Don't call it every time, because walking the entire
- * heap is a lot of overhead to free a few hundred bytes.
- */
- if (loopCount <= 0) {
- ZygoteInit.gc();
- loopCount = ZygoteInit.GC_LOOP_COUNT;
- } else {
- loopCount--;
- }
-
- if (runOnce()) {
- break;
- }
- }
- }
-
- /**
* Reads one start command from the command socket. If successful,
* a child is forked and a {@link ZygoteInit.MethodAndArgsCaller}
* exception is thrown in that child while in the parent process,
@@ -197,6 +160,11 @@
try {
parsedArgs = new Arguments(args);
+
+ if (parsedArgs.abiListQuery) {
+ return handleAbiListQuery();
+ }
+
if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
throw new ZygoteSecurityException("Client may not specify capabilities: " +
"permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
@@ -288,6 +256,18 @@
}
}
+ private boolean handleAbiListQuery() {
+ try {
+ final byte[] abiListBytes = abiList.getBytes(StandardCharsets.US_ASCII);
+ mSocketOutStream.writeInt(abiListBytes.length);
+ mSocketOutStream.write(abiListBytes);
+ return false;
+ } catch (IOException ioe) {
+ Log.e(TAG, "Error writing to command socket", ioe);
+ return true;
+ }
+ }
+
/**
* Closes socket associated with this connection.
*/
@@ -389,6 +369,11 @@
String remainingArgs[];
/**
+ * Whether the current arguments constitute an ABI list query.
+ */
+ boolean abiListQuery;
+
+ /**
* Constructs instance and parses args
* @param args zygote command-line args
* @throws IllegalArgumentException
@@ -541,6 +526,8 @@
mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
} else if (arg.equals("--mount-external-multiuser-all")) {
mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL;
+ } else if (arg.equals("--query-abi-list")) {
+ abiListQuery = true;
} else {
break;
}
@@ -807,7 +794,7 @@
/**
* Applies invoke-with system properties to the zygote arguments.
*
- * @param parsedArgs non-null; zygote args
+ * @param args non-null; zygote args
*/
public static void applyInvokeWithSystemProperty(Arguments args) {
if (args.invokeWith == null && args.niceName != null) {
@@ -976,7 +963,7 @@
mSocketOutStream.writeInt(pid);
mSocketOutStream.writeBoolean(usingWrapper);
} catch (IOException ex) {
- Log.e(TAG, "Error reading from command socket", ex);
+ Log.e(TAG, "Error writing to command socket", ex);
return true;
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index cc24ff7..495cc5f 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -32,7 +32,6 @@
import android.util.Log;
import dalvik.system.VMRuntime;
-import dalvik.system.Zygote;
import libcore.io.IoUtils;
import libcore.io.Libcore;
@@ -65,7 +64,7 @@
private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
- private static final String ANDROID_SOCKET_ENV = "ANDROID_SOCKET_zygote";
+ private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
@@ -73,8 +72,9 @@
/** when preloading, GC after allocating this many bytes */
private static final int PRELOAD_GC_THRESHOLD = 50000;
- public static final String USAGE_STRING =
- " <\"start-system-server\"|\"\" for startSystemServer>";
+ private static final String ABI_LIST_ARG = "--abi-list=";
+
+ private static final String SOCKET_NAME_ARG = "--socket-name=";
private static LocalServerSocket sServerSocket;
@@ -151,15 +151,15 @@
*
* @throws RuntimeException when open fails
*/
- private static void registerZygoteSocket() {
+ private static void registerZygoteSocket(String socketName) {
if (sServerSocket == null) {
int fileDesc;
+ final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
- String env = System.getenv(ANDROID_SOCKET_ENV);
+ String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
- throw new RuntimeException(
- ANDROID_SOCKET_ENV + " unset or invalid", ex);
+ throw new RuntimeException(fullSocketName + " unset or invalid", ex);
}
try {
@@ -176,9 +176,9 @@
* Waits for and accepts a single command connection. Throws
* RuntimeException on failure.
*/
- private static ZygoteConnection acceptCommandPeer() {
+ private static ZygoteConnection acceptCommandPeer(String abiList) {
try {
- return new ZygoteConnection(sServerSocket.accept());
+ return new ZygoteConnection(sServerSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
@@ -568,7 +568,26 @@
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();
- registerZygoteSocket();
+ boolean startSystemServer = false;
+ String socketName = "zygote";
+ String abiList = null;
+ for (int i = 1; i < argv.length; i++) {
+ if ("start-system-server".equals(argv[i])) {
+ startSystemServer = true;
+ } else if (argv[i].startsWith(ABI_LIST_ARG)) {
+ abiList = argv[i].substring(ABI_LIST_ARG.length());
+ } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
+ socketName = argv[i].substring(SOCKET_NAME_ARG.length());
+ } else {
+ throw new RuntimeException("Unknown command line argument: " + argv[i]);
+ }
+ }
+
+ if (abiList == null) {
+ throw new RuntimeException("No ABI list supplied.");
+ }
+
+ registerZygoteSocket(socketName);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preload();
@@ -585,20 +604,12 @@
// Zygote.
Trace.setTracingEnabled(false);
- // If requested, start system server directly from Zygote
- if (argv.length != 2) {
- throw new RuntimeException(argv[0] + USAGE_STRING);
- }
-
- if (argv[1].equals("start-system-server")) {
+ if (startSystemServer) {
startSystemServer();
- } else if (!argv[1].equals("")) {
- throw new RuntimeException(argv[0] + USAGE_STRING);
}
Log.i(TAG, "Accepting command socket connections");
-
- runSelectLoop();
+ runSelectLoop(abiList);
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
@@ -618,7 +629,7 @@
* @throws MethodAndArgsCaller in a child process when a main() should
* be executed.
*/
- private static void runSelectLoop() throws MethodAndArgsCaller {
+ private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
FileDescriptor[] fdArray = new FileDescriptor[4];
@@ -657,7 +668,7 @@
if (index < 0) {
throw new RuntimeException("Error in select()");
} else if (index == 0) {
- ZygoteConnection newPeer = acceptCommandPeer();
+ ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index ac70738..fda4114 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -149,7 +149,8 @@
android_content_res_ObbScanner.cpp \
android_content_res_Configuration.cpp \
android_animation_PropertyValuesHolder.cpp \
- com_android_internal_net_NetworkStatsFactory.cpp
+ com_android_internal_net_NetworkStatsFactory.cpp \
+ com_android_internal_os_Zygote.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 649968e..362a54e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -177,6 +177,7 @@
extern int register_android_animation_PropertyValuesHolder(JNIEnv *env);
extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
extern int register_com_android_internal_net_NetworkStatsFactory(JNIEnv *env);
+extern int register_com_android_internal_os_Zygote(JNIEnv *env);
static AndroidRuntime* gCurRuntime = NULL;
@@ -227,9 +228,10 @@
/*static*/ JavaVM* AndroidRuntime::mJavaVM = NULL;
-
-AndroidRuntime::AndroidRuntime() :
- mExitWithoutCleanup(false)
+AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :
+ mExitWithoutCleanup(false),
+ mArgBlockStart(argBlockStart),
+ mArgBlockLength(argBlockLength)
{
SkGraphics::Init();
// this sets our preference for 16bit images during decode
@@ -264,13 +266,17 @@
return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
-status_t AndroidRuntime::callMain(const char* className,
- jclass clazz, int argc, const char* const argv[])
+void AndroidRuntime::setArgv0(const char* argv0) {
+ strlcpy(mArgBlockStart, argv0, mArgBlockLength);
+}
+
+status_t AndroidRuntime::callMain(const String8& className, jclass clazz,
+ const Vector<String8>& args)
{
JNIEnv* env;
jmethodID methodId;
- ALOGD("Calling main entry %s", className);
+ ALOGD("Calling main entry %s", className.string());
env = getJNIEnv();
if (clazz == NULL || env == NULL) {
@@ -279,7 +285,7 @@
methodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V");
if (methodId == NULL) {
- ALOGE("ERROR: could not find method %s.main(String[])\n", className);
+ ALOGE("ERROR: could not find method %s.main(String[])\n", className.string());
return UNKNOWN_ERROR;
}
@@ -290,11 +296,12 @@
jclass stringClass;
jobjectArray strArray;
+ const size_t numArgs = args.size();
stringClass = env->FindClass("java/lang/String");
- strArray = env->NewObjectArray(argc, stringClass, NULL);
+ strArray = env->NewObjectArray(numArgs, stringClass, NULL);
- for (int i = 0; i < argc; i++) {
- jstring argStr = env->NewStringUTF(argv[i]);
+ for (size_t i = 0; i < numArgs; i++) {
+ jstring argStr = env->NewStringUTF(args[i].string());
env->SetObjectArrayElement(strArray, i, argStr);
}
@@ -866,20 +873,23 @@
* Passes the main function two arguments, the class name and the specified
* options string.
*/
-void AndroidRuntime::start(const char* className, const char* options)
+void AndroidRuntime::start(const char* className, const Vector<String8>& options)
{
ALOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
className != NULL ? className : "(unknown)");
+ static const String8 startSystemServer("start-system-server");
+
/*
* 'startSystemServer == true' means runtime is obsolete and not run from
* init.rc anymore, so we print out the boot start event here.
*/
- if (strcmp(options, "start-system-server") == 0) {
- /* track our progress through the boot sequence */
- const int LOG_BOOT_PROGRESS_START = 3000;
- LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
- ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
+ for (size_t i = 0; i < options.size(); ++i) {
+ if (options[i] == startSystemServer) {
+ /* track our progress through the boot sequence */
+ const int LOG_BOOT_PROGRESS_START = 3000;
+ LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
+ }
}
const char* rootDir = getenv("ANDROID_ROOT");
@@ -920,17 +930,20 @@
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
- jstring optionsStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
- strArray = env->NewObjectArray(2, stringClass, NULL);
+ strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr);
- optionsStr = env->NewStringUTF(options);
- env->SetObjectArrayElement(strArray, 1, optionsStr);
+
+ for (size_t i = 0; i < options.size(); ++i) {
+ jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
+ assert(optionsStr != NULL);
+ env->SetObjectArrayElement(strArray, i + 1, optionsStr);
+ }
/*
* Start VM. This thread becomes the main thread of the VM, and will
@@ -1241,6 +1254,7 @@
REG_JNI(register_android_net_wifi_WifiNative),
REG_JNI(register_android_os_MemoryFile),
REG_JNI(register_com_android_internal_os_ZygoteInit),
+ REG_JNI(register_com_android_internal_os_Zygote),
REG_JNI(register_android_hardware_Camera),
REG_JNI(register_android_hardware_camera2_CameraMetadata),
REG_JNI(register_android_hardware_SensorManager),
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 13c1fc8..44bf7f8 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -114,7 +114,7 @@
}
}
- int32_t* yDivs = chunk->getXDivs();
+ int32_t* yDivs = chunk->getYDivs();
for (int i = 0; i < chunk->numYDivs; i++) {
yDivs[i] = int32_t(yDivs[i] * scale + 0.5f);
if (i > 0 && yDivs[i] == yDivs[i - 1]) {
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 92d253f..e2c78b1 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -69,7 +69,7 @@
size_t totalSizeToDelete = text.getSize() + desc->getSize();
mSize -= totalSizeToDelete;
if (mDebugEnabled) {
- ALOGD("Cache value %p deleted, size = %d", desc.get(), totalSizeToDelete);
+ ALOGD("Cache value %p deleted, size = %zu", desc.get(), totalSizeToDelete);
}
}
@@ -129,7 +129,7 @@
bool removedOne = mCache.removeOldest();
LOG_ALWAYS_FATAL_IF(!removedOne, "The cache is non-empty but we "
"failed to remove the oldest entry. "
- "mSize = %u, size = %u, mMaxSize = %u, mCache.size() = %u",
+ "mSize = %u, size = %zu, mMaxSize = %u, mCache.size() = %zu",
mSize, size, mMaxSize, mCache.size());
}
}
@@ -148,7 +148,7 @@
nsecs_t totalTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
ALOGD("CACHE MISS: Added entry %p "
"with start = %d, count = %d, contextCount = %d, "
- "entry size %d bytes, remaining space %d bytes"
+ "entry size %zu bytes, remaining space %d bytes"
" - Compute time %0.6f ms - Put time %0.6f ms - Text = '%s'",
value.get(), start, count, contextCount, size, mMaxSize - mSize,
value->getElapsedTime() * 0.000001f,
@@ -159,7 +159,7 @@
if (mDebugEnabled) {
ALOGD("CACHE MISS: Calculated but not storing entry because it is too big "
"with start = %d, count = %d, contextCount = %d, "
- "entry size %d bytes, remaining space %d bytes"
+ "entry size %zu bytes, remaining space %d bytes"
" - Compute time %0.6f ms - Text = '%s'",
start, count, contextCount, size, mMaxSize - mSize,
value->getElapsedTime() * 0.000001f,
@@ -204,7 +204,7 @@
ALOGD("------------------------------------------------");
ALOGD("pid : %d", getpid());
ALOGD("running : %.0f seconds", timeRunningInSec);
- ALOGD("entries : %d", cacheSize);
+ ALOGD("entries : %zu", cacheSize);
ALOGD("max size : %d bytes", mMaxSize);
ALOGD("used : %d bytes according to mSize", mSize);
ALOGD("remaining : %d bytes or %2.2f percent", mMaxSize - mSize, remainingPercent);
diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp
index 9f79f74..98f4bed 100644
--- a/core/jni/android_net_LocalSocketImpl.cpp
+++ b/core/jni/android_net_LocalSocketImpl.cpp
@@ -111,7 +111,7 @@
/* private native void listen_native(int fd, int backlog) throws IOException; */
static void
-socket_listen (JNIEnv *env, jobject object, jobject fileDescriptor, int backlog)
+socket_listen (JNIEnv *env, jobject object, jobject fileDescriptor, jint backlog)
{
int ret;
int fd;
@@ -231,7 +231,7 @@
}
static jint
-socket_getOption(JNIEnv *env, jobject object, jobject fileDescriptor, int optID)
+socket_getOption(JNIEnv *env, jobject object, jobject fileDescriptor, jint optID)
{
int ret, value;
int opt, level;
@@ -279,7 +279,7 @@
}
static void socket_setOption(
- JNIEnv *env, jobject object, jobject fileDescriptor, int optID,
+ JNIEnv *env, jobject object, jobject fileDescriptor, jint optID,
jint boolValue, jint intValue) {
int ret;
int optname;
diff --git a/core/jni/android_nio_utils.cpp b/core/jni/android_nio_utils.cpp
index 7cbbe12..59d6e41 100644
--- a/core/jni/android_nio_utils.cpp
+++ b/core/jni/android_nio_utils.cpp
@@ -37,7 +37,7 @@
gNioJNI.getBasePointerID, buffer);
if (pointer != 0L) {
*array = NULL;
- return (void *) (jint) pointer;
+ return reinterpret_cast<void *>(pointer);
}
*array = (jarray) _env->CallStaticObjectMethod(gNioJNI.nioAccessClass,
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index a041693..d4873d6 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -800,7 +800,7 @@
fprintf(fp, "Total memory: %zu\n", totalMemory);
fprintf(fp, "Allocation records: %zd\n", recordCount);
if (backtraceSize != BACKTRACE_SIZE) {
- fprintf(fp, "WARNING: mismatched backtrace sizes (%d vs. %d)\n",
+ fprintf(fp, "WARNING: mismatched backtrace sizes (%zu vs. %d)\n",
backtraceSize, BACKTRACE_SIZE);
}
fprintf(fp, "\n");
@@ -823,7 +823,11 @@
if (backtrace[bt] == 0) {
break;
} else {
+#ifdef __LP64__
+ fprintf(fp, " %016x", backtrace[bt]);
+#else
fprintf(fp, " %08x", backtrace[bt]);
+#endif
}
}
fprintf(fp, "\n");
diff --git a/core/jni/android_server_NetworkManagementSocketTagger.cpp b/core/jni/android_server_NetworkManagementSocketTagger.cpp
index 12beff7..7e12b1e 100644
--- a/core/jni/android_server_NetworkManagementSocketTagger.cpp
+++ b/core/jni/android_server_NetworkManagementSocketTagger.cpp
@@ -47,8 +47,8 @@
return (jint)res;
}
-static int QTagUid_untagSocketFd(JNIEnv* env, jclass,
- jobject fileDescriptor) {
+static jint QTagUid_untagSocketFd(JNIEnv* env, jclass,
+ jobject fileDescriptor) {
int userFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
if (env->ExceptionOccurred() != NULL) {
diff --git a/core/jni/android_text_AndroidBidi.cpp b/core/jni/android_text_AndroidBidi.cpp
index d50a69f..6f7ee49 100644
--- a/core/jni/android_text_AndroidBidi.cpp
+++ b/core/jni/android_text_AndroidBidi.cpp
@@ -26,7 +26,7 @@
namespace android {
static jint runBidi(JNIEnv* env, jobject obj, jint dir, jcharArray chsArray,
- jbyteArray infoArray, int n, jboolean haveInfo)
+ jbyteArray infoArray, jint n, jboolean haveInfo)
{
// Parameters are checked on java side
// Failures from GetXXXArrayElements indicate a serious out-of-memory condition
diff --git a/core/jni/android_text_AndroidCharacter.cpp b/core/jni/android_text_AndroidCharacter.cpp
index 8b85a7b7..94bd40f 100644
--- a/core/jni/android_text_AndroidCharacter.cpp
+++ b/core/jni/android_text_AndroidCharacter.cpp
@@ -51,7 +51,8 @@
namespace android {
-static void getDirectionalities(JNIEnv* env, jobject obj, jcharArray srcArray, jbyteArray destArray, int count)
+static void getDirectionalities(JNIEnv* env, jobject obj, jcharArray srcArray,
+ jbyteArray destArray, jint count)
{
ScopedCharArrayRO src(env, srcArray);
if (src.get() == NULL) {
@@ -102,7 +103,7 @@
}
static void getEastAsianWidths(JNIEnv* env, jobject obj, jcharArray srcArray,
- int start, int count, jbyteArray destArray)
+ jint start, jint count, jbyteArray destArray)
{
ScopedCharArrayRO src(env, srcArray);
if (src.get() == NULL) {
@@ -144,20 +145,20 @@
}
}
-static jboolean mirror(JNIEnv* env, jobject obj, jcharArray charArray, int start, int count)
+static jboolean mirror(JNIEnv* env, jobject obj, jcharArray charArray, jint start, jint count)
{
ScopedCharArrayRW data(env, charArray);
if (data.get() == NULL) {
- return false;
+ return JNI_FALSE;
}
if (start < 0 || start > start + count
|| env->GetArrayLength(charArray) < start + count) {
jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
- return false;
+ return JNI_FALSE;
}
- bool ret = false;
+ jboolean ret = JNI_FALSE;
for (int i = start; i < start + count; i++) {
// XXX this thinks it knows that surrogates are never mirrored
@@ -166,7 +167,7 @@
if (c1 != c2) {
data[i] = c2;
- ret = true;
+ ret = JNI_TRUE;
}
}
return ret;
diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp
index aa2c5f39..28a8a5d 100644
--- a/core/jni/android_text_format_Time.cpp
+++ b/core/jni/android_text_format_Time.cpp
@@ -117,7 +117,7 @@
time2java(env, This, t);
RELEASE_TIMEZONE(This, t)
- return result;
+ return static_cast<jlong>(result);
}
static void android_text_format_Time_switchTimezone(JNIEnv* env, jobject This,
@@ -155,7 +155,7 @@
RELEASE_TIMEZONE(aObject, a)
RELEASE_TIMEZONE(bObject, b)
- return result;
+ return static_cast<jint>(result);
}
static jstring android_text_format_Time_format2445(JNIEnv* env, jobject This)
@@ -346,7 +346,7 @@
RELEASE_TIMEZONE(This, t)
- return result;
+ return static_cast<jlong>(result);
}
static void android_text_format_Time_set(JNIEnv* env, jobject This, jlong millis)
@@ -400,10 +400,10 @@
if (len < 8) {
jniThrowException(env, "android/util/TimeFormatException",
"String too short -- expected at least 8 characters.");
- return false;
+ return JNI_FALSE;
}
- jboolean inUtc = false;
+ jboolean inUtc = JNI_FALSE;
ScopedStringChars s(env, strObj);
@@ -414,49 +414,49 @@
n += get_char(env, s, 1, 100, &thrown);
n += get_char(env, s, 2, 10, &thrown);
n += get_char(env, s, 3, 1, &thrown);
- if (thrown) return false;
+ if (thrown) return JNI_FALSE;
env->SetIntField(This, g_yearField, n);
// month
n = get_char(env, s, 4, 10, &thrown);
n += get_char(env, s, 5, 1, &thrown);
n--;
- if (thrown) return false;
+ if (thrown) return JNI_FALSE;
env->SetIntField(This, g_monField, n);
// day of month
n = get_char(env, s, 6, 10, &thrown);
n += get_char(env, s, 7, 1, &thrown);
- if (thrown) return false;
+ if (thrown) return JNI_FALSE;
env->SetIntField(This, g_mdayField, n);
if (len > 8) {
// T
- if (!check_char(env, s, 8, 'T')) return false;
+ if (!check_char(env, s, 8, 'T')) return JNI_FALSE;
env->SetBooleanField(This, g_allDayField, JNI_FALSE);
// hour
n = get_char(env, s, 9, 10, &thrown);
n += get_char(env, s, 10, 1, &thrown);
- if (thrown) return false;
+ if (thrown) return JNI_FALSE;
env->SetIntField(This, g_hourField, n);
// min
n = get_char(env, s, 11, 10, &thrown);
n += get_char(env, s, 12, 1, &thrown);
- if (thrown) return false;
+ if (thrown) return JNI_FALSE;
env->SetIntField(This, g_minField, n);
// sec
n = get_char(env, s, 13, 10, &thrown);
n += get_char(env, s, 14, 1, &thrown);
- if (thrown) return false;
+ if (thrown) return JNI_FALSE;
env->SetIntField(This, g_secField, n);
if (len > 15) {
// Z
- if (!check_char(env, s, 15, 'Z')) return false;
- inUtc = true;
+ if (!check_char(env, s, 15, 'Z')) return JNI_FALSE;
+ inUtc = JNI_TRUE;
}
} else {
env->SetBooleanField(This, g_allDayField, JNI_TRUE);
@@ -481,10 +481,10 @@
if (len < 10) {
jniThrowException(env, "android/util/TimeFormatException",
"String too short --- expected at least 10 characters.");
- return false;
+ return JNI_FALSE;
}
- jboolean inUtc = false;
+ jboolean inUtc = JNI_FALSE;
ScopedStringChars s(env, strObj);
@@ -495,57 +495,57 @@
n += get_char(env, s, 1, 100, &thrown);
n += get_char(env, s, 2, 10, &thrown);
n += get_char(env, s, 3, 1, &thrown);
- if (thrown) return false;
+ if (thrown) return JNI_FALSE;
env->SetIntField(This, g_yearField, n);
// -
- if (!check_char(env, s, 4, '-')) return false;
+ if (!check_char(env, s, 4, '-')) return JNI_FALSE;
// month
n = get_char(env, s, 5, 10, &thrown);
n += get_char(env, s, 6, 1, &thrown);
--n;
- if (thrown) return false;
+ if (thrown) return JNI_FALSE;
env->SetIntField(This, g_monField, n);
// -
- if (!check_char(env, s, 7, '-')) return false;
+ if (!check_char(env, s, 7, '-')) return JNI_FALSE;
// day
n = get_char(env, s, 8, 10, &thrown);
n += get_char(env, s, 9, 1, &thrown);
- if (thrown) return false;
+ if (thrown) return JNI_FALSE;
env->SetIntField(This, g_mdayField, n);
if (len >= 19) {
// T
- if (!check_char(env, s, 10, 'T')) return false;
+ if (!check_char(env, s, 10, 'T')) return JNI_FALSE;
env->SetBooleanField(This, g_allDayField, JNI_FALSE);
// hour
n = get_char(env, s, 11, 10, &thrown);
n += get_char(env, s, 12, 1, &thrown);
- if (thrown) return false;
+ if (thrown) return JNI_FALSE;
int hour = n;
// env->SetIntField(This, g_hourField, n);
// :
- if (!check_char(env, s, 13, ':')) return false;
+ if (!check_char(env, s, 13, ':')) return JNI_FALSE;
// minute
n = get_char(env, s, 14, 10, &thrown);
n += get_char(env, s, 15, 1, &thrown);
- if (thrown) return false;
+ if (thrown) return JNI_FALSE;
int minute = n;
// env->SetIntField(This, g_minField, n);
// :
- if (!check_char(env, s, 16, ':')) return false;
+ if (!check_char(env, s, 16, ':')) return JNI_FALSE;
// second
n = get_char(env, s, 17, 10, &thrown);
n += get_char(env, s, 18, 1, &thrown);
- if (thrown) return false;
+ if (thrown) return JNI_FALSE;
env->SetIntField(This, g_secField, n);
// skip the '.XYZ' -- we don't care about subsecond precision.
@@ -579,32 +579,32 @@
jniThrowExceptionFmt(env, "android/util/TimeFormatException",
"Unexpected character 0x%02x at position %d. Expected + or -",
c, tz_index);
- return false;
+ return JNI_FALSE;
}
- inUtc = true;
+ inUtc = JNI_TRUE;
if (offset != 0) {
if (len < tz_index + 6) {
jniThrowExceptionFmt(env, "android/util/TimeFormatException",
"Unexpected length; should be %d characters",
tz_index + 6);
- return false;
+ return JNI_FALSE;
}
// hour
n = get_char(env, s, tz_index + 1, 10, &thrown);
n += get_char(env, s, tz_index + 2, 1, &thrown);
- if (thrown) return false;
+ if (thrown) return JNI_FALSE;
n *= offset;
hour += n;
// :
- if (!check_char(env, s, tz_index + 3, ':')) return false;
+ if (!check_char(env, s, tz_index + 3, ':')) return JNI_FALSE;
// minute
n = get_char(env, s, tz_index + 4, 10, &thrown);
n += get_char(env, s, tz_index + 5, 1, &thrown);
- if (thrown) return false;
+ if (thrown) return JNI_FALSE;
n *= offset;
minute += n;
}
diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp
index 2593420..8a0eaa2 100644
--- a/core/jni/android_util_EventLog.cpp
+++ b/core/jni/android_util_EventLog.cpp
@@ -177,13 +177,13 @@
break;
}
if (ret < 0) {
- if (errno == EINTR) {
+ if (ret == -EINTR) {
continue;
}
- if (errno == EINVAL) {
+ if (ret == -EINVAL) {
jniThrowException(env, "java/io/IOException", "Event too short");
- } else if (errno != EAGAIN) {
- jniThrowIOException(env, errno); // Will throw on return
+ } else if (ret != -EAGAIN) {
+ jniThrowIOException(env, -ret); // Will throw on return
}
break;
}
diff --git a/core/jni/android_util_Log.cpp b/core/jni/android_util_Log.cpp
index 536a582..93dcbef 100644
--- a/core/jni/android_util_Log.cpp
+++ b/core/jni/android_util_Log.cpp
@@ -85,7 +85,7 @@
jboolean result = false;
if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {
char buf2[200];
- snprintf(buf2, sizeof(buf2), "Log tag \"%s\" exceeds limit of %d characters\n",
+ snprintf(buf2, sizeof(buf2), "Log tag \"%s\" exceeds limit of %zu characters\n",
chars, PROPERTY_KEY_MAX - sizeof(LOG_NAMESPACE));
jniThrowException(env, "java/lang/IllegalArgumentException", buf2);
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 601975a..9f8eb34 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -19,8 +19,8 @@
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
+#include <cutils/process_name.h>
#include <cutils/sched_policy.h>
#include <utils/String8.h>
#include <utils/Vector.h>
@@ -402,7 +402,9 @@
}
if (name8.size() > 0) {
- ProcessState::self()->setArgV0(name8.string());
+ const char* procName = name8.string();
+ set_process_name(procName);
+ AndroidRuntime::getRuntime()->setArgv0(procName);
}
}
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index a860918..2004576 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -19,11 +19,12 @@
#include <android_runtime/AndroidRuntime.h>
-#include <utils/Log.h>
-#include <androidfw/ZipFileRO.h>
-#include <androidfw/ZipUtils.h>
#include <ScopedUtfChars.h>
#include <UniquePtr.h>
+#include <androidfw/ZipFileRO.h>
+#include <androidfw/ZipUtils.h>
+#include <utils/Log.h>
+#include <utils/Vector.h>
#include <zlib.h>
@@ -54,17 +55,19 @@
namespace android {
// These match PackageManager.java install codes
-typedef enum {
+enum install_status_t {
INSTALL_SUCCEEDED = 1,
INSTALL_FAILED_INVALID_APK = -2,
INSTALL_FAILED_INSUFFICIENT_STORAGE = -4,
INSTALL_FAILED_CONTAINER_ERROR = -18,
INSTALL_FAILED_INTERNAL_ERROR = -110,
-} install_status_t;
+ INSTALL_FAILED_NO_MATCHING_ABIS = -112,
+ NO_NATIVE_LIBRARIES = -113
+};
typedef install_status_t (*iterFunc)(JNIEnv*, void*, ZipFileRO*, ZipEntryRO, const char*);
-// Equivalent to isFilenameSafe
+// Equivalent to android.os.FileUtils.isFilenameSafe
static bool
isFilenameSafe(const char* filename)
{
@@ -268,126 +271,252 @@
return INSTALL_SUCCEEDED;
}
-static install_status_t
-iterateOverNativeFiles(JNIEnv *env, jstring javaFilePath, jstring javaCpuAbi, jstring javaCpuAbi2,
- iterFunc callFunc, void* callArg) {
- ScopedUtfChars filePath(env, javaFilePath);
- ScopedUtfChars cpuAbi(env, javaCpuAbi);
- ScopedUtfChars cpuAbi2(env, javaCpuAbi2);
-
- UniquePtr<ZipFileRO> zipFile(ZipFileRO::open(filePath.c_str()));
- if (zipFile.get() == NULL) {
- ALOGI("Couldn't open APK %s\n", filePath.c_str());
- return INSTALL_FAILED_INVALID_APK;
+/*
+ * An iterator over all shared libraries in a zip file. An entry is
+ * considered to be a shared library if all of the conditions below are
+ * satisfied :
+ *
+ * - The entry is under the lib/ directory.
+ * - The entry name ends with ".so" and the entry name starts with "lib",
+ * an exception is made for entries whose name is "gdbserver".
+ * - The entry filename is "safe" (as determined by isFilenameSafe).
+ *
+ */
+class NativeLibrariesIterator {
+private:
+ NativeLibrariesIterator(ZipFileRO* zipFile, void* cookie)
+ : mZipFile(zipFile), mCookie(cookie), mLastSlash(NULL) {
+ fileName[0] = '\0';
}
+public:
+ static NativeLibrariesIterator* create(ZipFileRO* zipFile) {
+ void* cookie = NULL;
+ if (!zipFile->startIteration(&cookie)) {
+ return NULL;
+ }
+
+ return new NativeLibrariesIterator(zipFile, cookie);
+ }
+
+ ZipEntryRO next() {
+ ZipEntryRO next = NULL;
+ while ((next = mZipFile->nextEntry(mCookie)) != NULL) {
+ // Make sure this entry has a filename.
+ if (mZipFile->getEntryFileName(next, fileName, sizeof(fileName))) {
+ continue;
+ }
+
+ // Make sure we're in the lib directory of the ZIP.
+ if (strncmp(fileName, APK_LIB, APK_LIB_LEN)) {
+ continue;
+ }
+
+ // Make sure the filename is at least to the minimum library name size.
+ const size_t fileNameLen = strlen(fileName);
+ static const size_t minLength = APK_LIB_LEN + 2 + LIB_PREFIX_LEN + 1 + LIB_SUFFIX_LEN;
+ if (fileNameLen < minLength) {
+ continue;
+ }
+
+ const char* lastSlash = strrchr(fileName, '/');
+ ALOG_ASSERT(lastSlash != NULL, "last slash was null somehow for %s\n", fileName);
+
+ // Exception: If we find the gdbserver binary, return it.
+ if (!strncmp(lastSlash + 1, GDBSERVER, GDBSERVER_LEN)) {
+ break;
+ }
+
+ // Make sure the filename starts with lib and ends with ".so".
+ if (strncmp(fileName + fileNameLen - LIB_SUFFIX_LEN, LIB_SUFFIX, LIB_SUFFIX_LEN)
+ || strncmp(lastSlash, LIB_PREFIX, LIB_PREFIX_LEN)) {
+ continue;
+ }
+
+ // Make sure the filename is safe.
+ if (!isFilenameSafe(lastSlash + 1)) {
+ continue;
+ }
+
+ mLastSlash = lastSlash;
+ break;
+ }
+
+ return next;
+ }
+
+ inline const char* currentEntry() const {
+ return fileName;
+ }
+
+ inline const char* lastSlash() const {
+ return mLastSlash;
+ }
+
+ virtual ~NativeLibrariesIterator() {
+ mZipFile->endIteration(mCookie);
+ }
+private:
+
char fileName[PATH_MAX];
- bool hasPrimaryAbi = false;
+ ZipFileRO* const mZipFile;
+ void* mCookie;
+ const char* mLastSlash;
+};
- void* cookie = NULL;
- if (!zipFile->startIteration(&cookie)) {
- ALOGI("Couldn't iterate over APK%s\n", filePath.c_str());
+static install_status_t
+iterateOverNativeFiles(JNIEnv *env, jlong apkHandle, jstring javaCpuAbi,
+ iterFunc callFunc, void* callArg) {
+ ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
+ if (zipFile == NULL) {
return INSTALL_FAILED_INVALID_APK;
}
+ UniquePtr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile));
+ if (it.get() == NULL) {
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+ const ScopedUtfChars cpuAbi(env, javaCpuAbi);
+ if (cpuAbi.c_str() == NULL) {
+ // This would've thrown, so this return code isn't observable by
+ // Java.
+ return INSTALL_FAILED_INVALID_APK;
+ }
ZipEntryRO entry = NULL;
- while ((entry = zipFile->nextEntry(cookie)) != NULL) {
- // Make sure this entry has a filename.
- if (zipFile->getEntryFileName(entry, fileName, sizeof(fileName))) {
- continue;
- }
-
- // Make sure we're in the lib directory of the ZIP.
- if (strncmp(fileName, APK_LIB, APK_LIB_LEN)) {
- continue;
- }
-
- // Make sure the filename is at least to the minimum library name size.
- const size_t fileNameLen = strlen(fileName);
- static const size_t minLength = APK_LIB_LEN + 2 + LIB_PREFIX_LEN + 1 + LIB_SUFFIX_LEN;
- if (fileNameLen < minLength) {
- continue;
- }
-
- const char* lastSlash = strrchr(fileName, '/');
- ALOG_ASSERT(lastSlash != NULL, "last slash was null somehow for %s\n", fileName);
+ while ((entry = it->next()) != NULL) {
+ const char* fileName = it->currentEntry();
+ const char* lastSlash = it->lastSlash();
// Check to make sure the CPU ABI of this file is one we support.
const char* cpuAbiOffset = fileName + APK_LIB_LEN;
const size_t cpuAbiRegionSize = lastSlash - cpuAbiOffset;
- ALOGV("Comparing ABIs %s and %s versus %s\n", cpuAbi.c_str(), cpuAbi2.c_str(), cpuAbiOffset);
- if (cpuAbi.size() == cpuAbiRegionSize
- && *(cpuAbiOffset + cpuAbi.size()) == '/'
- && !strncmp(cpuAbiOffset, cpuAbi.c_str(), cpuAbiRegionSize)) {
- ALOGV("Using primary ABI %s\n", cpuAbi.c_str());
- hasPrimaryAbi = true;
- } else if (cpuAbi2.size() == cpuAbiRegionSize
- && *(cpuAbiOffset + cpuAbi2.size()) == '/'
- && !strncmp(cpuAbiOffset, cpuAbi2.c_str(), cpuAbiRegionSize)) {
-
- /*
- * If this library matches both the primary and secondary ABIs,
- * only use the primary ABI.
- */
- if (hasPrimaryAbi) {
- ALOGV("Already saw primary ABI, skipping secondary ABI %s\n", cpuAbi2.c_str());
- continue;
- } else {
- ALOGV("Using secondary ABI %s\n", cpuAbi2.c_str());
- }
- } else {
- ALOGV("abi didn't match anything: %s (end at %zd)\n", cpuAbiOffset, cpuAbiRegionSize);
- continue;
- }
-
- // If this is a .so file, check to see if we need to copy it.
- if ((!strncmp(fileName + fileNameLen - LIB_SUFFIX_LEN, LIB_SUFFIX, LIB_SUFFIX_LEN)
- && !strncmp(lastSlash, LIB_PREFIX, LIB_PREFIX_LEN)
- && isFilenameSafe(lastSlash + 1))
- || !strncmp(lastSlash + 1, GDBSERVER, GDBSERVER_LEN)) {
-
- install_status_t ret = callFunc(env, callArg, zipFile.get(), entry, lastSlash + 1);
+ if (cpuAbi.size() == cpuAbiRegionSize && !strncmp(cpuAbiOffset, cpuAbi.c_str(), cpuAbiRegionSize)) {
+ install_status_t ret = callFunc(env, callArg, zipFile, entry, lastSlash + 1);
if (ret != INSTALL_SUCCEEDED) {
ALOGV("Failure for entry %s", lastSlash + 1);
- zipFile->endIteration(cookie);
return ret;
}
}
}
- zipFile->endIteration(cookie);
-
return INSTALL_SUCCEEDED;
}
+
+static int findSupportedAbi(JNIEnv *env, jlong apkHandle, jobjectArray supportedAbisArray) {
+ const int numAbis = env->GetArrayLength(supportedAbisArray);
+ Vector<ScopedUtfChars*> supportedAbis;
+
+ for (int i = 0; i < numAbis; ++i) {
+ supportedAbis.add(new ScopedUtfChars(env,
+ (jstring) env->GetObjectArrayElement(supportedAbisArray, i)));
+ }
+
+ ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
+ if (zipFile == NULL) {
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+ UniquePtr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile));
+ if (it.get() == NULL) {
+ return INSTALL_FAILED_INVALID_APK;
+ }
+
+ ZipEntryRO entry = NULL;
+ char fileName[PATH_MAX];
+ int status = NO_NATIVE_LIBRARIES;
+ while ((entry = it->next()) != NULL) {
+ // We're currently in the lib/ directory of the APK, so it does have some native
+ // code. We should return INSTALL_FAILED_NO_MATCHING_ABIS if none of the
+ // libraries match.
+ if (status == NO_NATIVE_LIBRARIES) {
+ status = INSTALL_FAILED_NO_MATCHING_ABIS;
+ }
+
+ const char* fileName = it->currentEntry();
+ const char* lastSlash = it->lastSlash();
+
+ // Check to see if this CPU ABI matches what we are looking for.
+ const char* abiOffset = fileName + APK_LIB_LEN;
+ const size_t abiSize = lastSlash - abiOffset;
+ for (int i = 0; i < numAbis; i++) {
+ const ScopedUtfChars* abi = supportedAbis[i];
+ if (abi->size() == abiSize && !strncmp(abiOffset, abi->c_str(), abiSize)) {
+ // The entry that comes in first (i.e. with a lower index) has the higher priority.
+ if (((i < status) && (status >= 0)) || (status < 0) ) {
+ status = i;
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < numAbis; ++i) {
+ delete supportedAbis[i];
+ }
+
+ return status;
+}
+
static jint
com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env, jclass clazz,
- jstring javaFilePath, jstring javaNativeLibPath, jstring javaCpuAbi, jstring javaCpuAbi2)
+ jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi)
{
- return (jint) iterateOverNativeFiles(env, javaFilePath, javaCpuAbi, javaCpuAbi2,
+ return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi,
copyFileIfChanged, &javaNativeLibPath);
}
static jlong
com_android_internal_content_NativeLibraryHelper_sumNativeBinaries(JNIEnv *env, jclass clazz,
- jstring javaFilePath, jstring javaCpuAbi, jstring javaCpuAbi2)
+ jlong apkHandle, jstring javaCpuAbi)
{
size_t totalSize = 0;
- iterateOverNativeFiles(env, javaFilePath, javaCpuAbi, javaCpuAbi2, sumFiles, &totalSize);
+ iterateOverNativeFiles(env, apkHandle, javaCpuAbi, sumFiles, &totalSize);
return totalSize;
}
+static jint
+com_android_internal_content_NativeLibraryHelper_findSupportedAbi(JNIEnv *env, jclass clazz,
+ jlong apkHandle, jobjectArray javaCpuAbisToSearch)
+{
+ return (jint) findSupportedAbi(env, apkHandle, javaCpuAbisToSearch);
+}
+
+static jlong
+com_android_internal_content_NativeLibraryHelper_openApk(JNIEnv *env, jclass, jstring apkPath)
+{
+ ScopedUtfChars filePath(env, apkPath);
+ ZipFileRO* zipFile = ZipFileRO::open(filePath.c_str());
+
+ return reinterpret_cast<jlong>(zipFile);
+}
+
+static void
+com_android_internal_content_NativeLibraryHelper_close(JNIEnv *env, jclass, jlong apkHandle)
+{
+ delete reinterpret_cast<ZipFileRO*>(apkHandle);
+}
+
static JNINativeMethod gMethods[] = {
+ {"nativeOpenApk",
+ "(Ljava/lang/String;)J",
+ (void *)com_android_internal_content_NativeLibraryHelper_openApk},
+ {"nativeClose",
+ "(J)V",
+ (void *)com_android_internal_content_NativeLibraryHelper_close},
{"nativeCopyNativeBinaries",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ "(JLjava/lang/String;Ljava/lang/String;)I",
(void *)com_android_internal_content_NativeLibraryHelper_copyNativeBinaries},
{"nativeSumNativeBinaries",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J",
+ "(JLjava/lang/String;)J",
(void *)com_android_internal_content_NativeLibraryHelper_sumNativeBinaries},
+ {"nativeFindSupportedAbi",
+ "(J[Ljava/lang/String;)I",
+ (void *)com_android_internal_content_NativeLibraryHelper_findSupportedAbi},
};
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
new file mode 100644
index 0000000..a61fa87
--- /dev/null
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -0,0 +1,608 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#include "android_runtime/AndroidRuntime.h"
+
+// sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc
+#include <sys/mount.h>
+#include <linux/fs.h>
+
+#include <grp.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "cutils/fs.h"
+#include "cutils/multiuser.h"
+#include "cutils/sched_policy.h"
+#include "utils/String8.h"
+#include "JNIHelp.h"
+#include "ScopedLocalRef.h"
+#include "ScopedPrimitiveArray.h"
+#include "ScopedUtfChars.h"
+
+#if defined(HAVE_PRCTL)
+#include <sys/prctl.h>
+#endif
+
+#include <selinux/android.h>
+
+#if defined(__linux__)
+#include <sys/personality.h>
+#include <sys/utsname.h>
+#if defined(HAVE_ANDROID_OS)
+#include <sys/capability.h>
+#endif
+#endif
+
+namespace {
+
+using android::String8;
+
+static pid_t gSystemServerPid = 0;
+
+static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
+static jclass gZygoteClass;
+static jmethodID gCallPostForkChildHooks;
+
+// Must match values in com.android.internal.os.Zygote.
+enum MountExternalKind {
+ MOUNT_EXTERNAL_NONE = 0,
+ MOUNT_EXTERNAL_SINGLEUSER = 1,
+ MOUNT_EXTERNAL_MULTIUSER = 2,
+ MOUNT_EXTERNAL_MULTIUSER_ALL = 3,
+};
+
+static void RuntimeAbort(JNIEnv* env) {
+ env->FatalError("RuntimeAbort");
+}
+
+// This signal handler is for zygote mode, since the zygote must reap its children
+static void SigChldHandler(int /*signal_number*/) {
+ pid_t pid;
+ int status;
+
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+ // Log process-death status that we care about. In general it is
+ // not safe to call LOG(...) from a signal handler because of
+ // possible reentrancy. However, we know a priori that the
+ // current implementation of LOG() is safe to call from a SIGCHLD
+ // handler in the zygote process. If the LOG() implementation
+ // changes its locking strategy or its use of syscalls within the
+ // lazy-init critical section, its use here may become unsafe.
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status)) {
+ ALOGI("Process %d exited cleanly (%d)", pid, WEXITSTATUS(status));
+ } else if (false) {
+ ALOGI("Process %d exited cleanly (%d)", pid, WEXITSTATUS(status));
+ }
+ } else if (WIFSIGNALED(status)) {
+ if (WTERMSIG(status) != SIGKILL) {
+ ALOGI("Process %d exited cleanly (%d)", pid, WTERMSIG(status));
+ } else if (false) {
+ ALOGI("Process %d exited cleanly (%d)", pid, WTERMSIG(status));
+ }
+#ifdef WCOREDUMP
+ if (WCOREDUMP(status)) {
+ ALOGI("Process %d dumped core.", pid);
+ }
+#endif /* ifdef WCOREDUMP */
+ }
+
+ // If the just-crashed process is the system_server, bring down zygote
+ // so that it is restarted by init and system server will be restarted
+ // from there.
+ if (pid == gSystemServerPid) {
+ ALOGE("Exit zygote because system server (%d) has terminated");
+ kill(getpid(), SIGKILL);
+ }
+ }
+
+ if (pid < 0) {
+ ALOGW("Zygote SIGCHLD error in waitpid: %d", errno);
+ }
+}
+
+// Configures the SIGCHLD handler for the zygote process. This is configured
+// very late, because earlier in the runtime we may fork() and exec()
+// other processes, and we want to waitpid() for those rather than
+// have them be harvested immediately.
+//
+// This ends up being called repeatedly before each fork(), but there's
+// no real harm in that.
+static void SetSigChldHandler() {
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SigChldHandler;
+
+ int err = sigaction(SIGCHLD, &sa, NULL);
+ if (err < 0) {
+ ALOGW("Error setting SIGCHLD handler: %d", errno);
+ }
+}
+
+// Sets the SIGCHLD handler back to default behavior in zygote children.
+static void UnsetSigChldHandler() {
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+
+ int err = sigaction(SIGCHLD, &sa, NULL);
+ if (err < 0) {
+ ALOGW("Error unsetting SIGCHLD handler: %d", errno);
+ }
+}
+
+// Calls POSIX setgroups() using the int[] object as an argument.
+// A NULL argument is tolerated.
+static void SetGids(JNIEnv* env, jintArray javaGids) {
+ if (javaGids == NULL) {
+ return;
+ }
+
+ ScopedIntArrayRO gids(env, javaGids);
+ if (gids.get() == NULL) {
+ RuntimeAbort(env);
+ }
+ int rc = setgroups(gids.size(), reinterpret_cast<const gid_t*>(&gids[0]));
+ if (rc == -1) {
+ ALOGE("setgroups failed");
+ RuntimeAbort(env);
+ }
+}
+
+// Sets the resource limits via setrlimit(2) for the values in the
+// two-dimensional array of integers that's passed in. The second dimension
+// contains a tuple of length 3: (resource, rlim_cur, rlim_max). NULL is
+// treated as an empty array.
+static void SetRLimits(JNIEnv* env, jobjectArray javaRlimits) {
+ if (javaRlimits == NULL) {
+ return;
+ }
+
+ rlimit rlim;
+ memset(&rlim, 0, sizeof(rlim));
+
+ for (int i = 0; i < env->GetArrayLength(javaRlimits); ++i) {
+ ScopedLocalRef<jobject> javaRlimitObject(env, env->GetObjectArrayElement(javaRlimits, i));
+ ScopedIntArrayRO javaRlimit(env, reinterpret_cast<jintArray>(javaRlimitObject.get()));
+ if (javaRlimit.size() != 3) {
+ ALOGE("rlimits array must have a second dimension of size 3");
+ RuntimeAbort(env);
+ }
+
+ rlim.rlim_cur = javaRlimit[1];
+ rlim.rlim_max = javaRlimit[2];
+
+ int rc = setrlimit(javaRlimit[0], &rlim);
+ if (rc == -1) {
+ ALOGE("setrlimit(%d, {%d, %d}) failed", javaRlimit[0], rlim.rlim_cur, rlim.rlim_max);
+ RuntimeAbort(env);
+ }
+ }
+}
+
+#if defined(HAVE_ANDROID_OS)
+
+// The debug malloc library needs to know whether it's the zygote or a child.
+extern "C" int gMallocLeakZygoteChild;
+
+static void EnableKeepCapabilities(JNIEnv* env) {
+ int rc = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
+ if (rc == -1) {
+ ALOGE("prctl(PR_SET_KEEPCAPS) failed");
+ RuntimeAbort(env);
+ }
+}
+
+static void DropCapabilitiesBoundingSet(JNIEnv* env) {
+ for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
+ int rc = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
+ if (rc == -1) {
+ if (errno == EINVAL) {
+ ALOGE("prctl(PR_CAPBSET_DROP) failed with EINVAL. Please verify "
+ "your kernel is compiled with file capabilities support");
+ } else {
+ ALOGE("prctl(PR_CAPBSET_DROP) failed");
+ RuntimeAbort(env);
+ }
+ }
+ }
+}
+
+static void SetCapabilities(JNIEnv* env, int64_t permitted, int64_t effective) {
+ __user_cap_header_struct capheader;
+ memset(&capheader, 0, sizeof(capheader));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capheader.pid = 0;
+
+ __user_cap_data_struct capdata[2];
+ memset(&capdata, 0, sizeof(capdata));
+ capdata[0].effective = effective;
+ capdata[1].effective = effective >> 32;
+ capdata[0].permitted = permitted;
+ capdata[1].permitted = permitted >> 32;
+
+ if (capset(&capheader, &capdata[0]) == -1) {
+ ALOGE("capset(%lld, %lld) failed", permitted, effective);
+ RuntimeAbort(env);
+ }
+}
+
+static void SetSchedulerPolicy(JNIEnv* env) {
+ errno = -set_sched_policy(0, SP_DEFAULT);
+ if (errno != 0) {
+ ALOGE("set_sched_policy(0, SP_DEFAULT) failed");
+ RuntimeAbort(env);
+ }
+}
+
+#else
+
+static int gMallocLeakZygoteChild = 0;
+
+static void EnableKeepCapabilities(JNIEnv*) {}
+static void DropCapabilitiesBoundingSet(JNIEnv*) {}
+static void SetCapabilities(JNIEnv*, int64_t, int64_t) {}
+static void SetSchedulerPolicy(JNIEnv*) {}
+
+#endif
+
+// Create a private mount namespace and bind mount appropriate emulated
+// storage for the given user.
+static bool MountEmulatedStorage(uid_t uid, jint mount_mode) {
+ if (mount_mode == MOUNT_EXTERNAL_NONE) {
+ return true;
+ }
+
+ // See storage config details at http://source.android.com/tech/storage/
+ userid_t user_id = multiuser_get_user_id(uid);
+
+ // Create a second private mount namespace for our process
+ if (unshare(CLONE_NEWNS) == -1) {
+ ALOGW("Failed to unshare(): %d", errno);
+ return false;
+ }
+
+ // Create bind mounts to expose external storage
+ if (mount_mode == MOUNT_EXTERNAL_MULTIUSER || mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) {
+ // These paths must already be created by init.rc
+ const char* source = getenv("EMULATED_STORAGE_SOURCE");
+ const char* target = getenv("EMULATED_STORAGE_TARGET");
+ const char* legacy = getenv("EXTERNAL_STORAGE");
+ if (source == NULL || target == NULL || legacy == NULL) {
+ ALOGW("Storage environment undefined; unable to provide external storage");
+ return false;
+ }
+
+ // Prepare source paths
+
+ // /mnt/shell/emulated/0
+ const String8 source_user(String8::format("%s/%d", source, user_id));
+ // /storage/emulated/0
+ const String8 target_user(String8::format("%s/%d", target, user_id));
+
+ if (fs_prepare_dir(source_user.string(), 0000, 0, 0) == -1
+ || fs_prepare_dir(target_user.string(), 0000, 0, 0) == -1) {
+ return false;
+ }
+
+ if (mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) {
+ // Mount entire external storage tree for all users
+ if (TEMP_FAILURE_RETRY(mount(source, target, NULL, MS_BIND, NULL)) == -1) {
+ ALOGW("Failed to mount %s to %s :%d", source, target, errno);
+ return false;
+ }
+ } else {
+ // Only mount user-specific external storage
+ if (TEMP_FAILURE_RETRY(
+ mount(source_user.string(), target_user.string(), NULL, MS_BIND, NULL)) == -1) {
+ ALOGW("Failed to mount %s to %s: %d", source_user.string(), target_user.string(), errno);
+ return false;
+ }
+ }
+
+ if (fs_prepare_dir(legacy, 0000, 0, 0) == -1) {
+ return false;
+ }
+
+ // Finally, mount user-specific path into place for legacy users
+ if (TEMP_FAILURE_RETRY(
+ mount(target_user.string(), legacy, NULL, MS_BIND | MS_REC, NULL)) == -1) {
+ ALOGW("Failed to mount %s to %s: %d", target_user.string(), legacy, errno);
+ return false;
+ }
+ } else {
+ ALOGW("Mount mode %d unsupported", mount_mode);
+ return false;
+ }
+
+ return true;
+}
+
+#if defined(__linux__)
+static bool NeedsNoRandomizeWorkaround() {
+#if !defined(__arm__)
+ return false;
+#else
+ int major;
+ int minor;
+ struct utsname uts;
+ if (uname(&uts) == -1) {
+ return false;
+ }
+
+ if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
+ return false;
+ }
+
+ // Kernels before 3.4.* need the workaround.
+ return (major < 3) || ((major == 3) && (minor < 4));
+#endif
+}
+#endif
+
+// Utility to close down the Zygote socket file descriptors while
+// the child is still running as root with Zygote's privileges. Each
+// descriptor (if any) is closed via dup2(), replacing it with a valid
+// (open) descriptor to /dev/null.
+
+static void DetachDescriptors(JNIEnv* env, jintArray fdsToClose) {
+ if (!fdsToClose) {
+ return;
+ }
+ jsize count = env->GetArrayLength(fdsToClose);
+ jint *ar = env->GetIntArrayElements(fdsToClose, 0);
+ if (!ar) {
+ ALOGE("Bad fd array");
+ RuntimeAbort(env);
+ }
+ jsize i;
+ int devnull;
+ for (i = 0; i < count; i++) {
+ devnull = open("/dev/null", O_RDWR);
+ if (devnull < 0) {
+ ALOGE("Failed to open /dev/null");
+ RuntimeAbort(env);
+ continue;
+ }
+ ALOGV("Switching descriptor %d to /dev/null: %d", ar[i], errno);
+ if (dup2(devnull, ar[i]) < 0) {
+ ALOGE("Failed dup2() on descriptor %d", ar[i]);
+ RuntimeAbort(env);
+ }
+ close(devnull);
+ }
+}
+
+void SetThreadName(const char* thread_name) {
+ bool hasAt = false;
+ bool hasDot = false;
+ const char* s = thread_name;
+ while (*s) {
+ if (*s == '.') {
+ hasDot = true;
+ } else if (*s == '@') {
+ hasAt = true;
+ }
+ s++;
+ }
+ const int len = s - thread_name;
+ if (len < 15 || hasAt || !hasDot) {
+ s = thread_name;
+ } else {
+ s = thread_name + len - 15;
+ }
+ // pthread_setname_np fails rather than truncating long strings.
+ char buf[16]; // MAX_TASK_COMM_LEN=16 is hard-coded into bionic
+ strlcpy(buf, s, sizeof(buf)-1);
+ errno = pthread_setname_np(pthread_self(), buf);
+ if (errno != 0) {
+ ALOGW("Unable to set the name of current thread to '%s'", buf);
+ }
+}
+
+// Utility routine to fork zygote and specialize the child process.
+static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
+ jint debug_flags, jobjectArray javaRlimits,
+ jlong permittedCapabilities, jlong effectiveCapabilities,
+ jint mount_external,
+ jstring java_se_info, jstring java_se_name,
+ bool is_system_server, jintArray fdsToClose) {
+ SetSigChldHandler();
+
+ pid_t pid = fork();
+
+ if (pid == 0) {
+ // The child process.
+ gMallocLeakZygoteChild = 1;
+
+ // Clean up any descriptors which must be closed immediately
+ DetachDescriptors(env, fdsToClose);
+
+ // Keep capabilities across UID change, unless we're staying root.
+ if (uid != 0) {
+ EnableKeepCapabilities(env);
+ }
+
+ DropCapabilitiesBoundingSet(env);
+
+ if (!MountEmulatedStorage(uid, mount_external)) {
+ ALOGW("Failed to mount emulated storage: %d", errno);
+ if (errno == ENOTCONN || errno == EROFS) {
+ // When device is actively encrypting, we get ENOTCONN here
+ // since FUSE was mounted before the framework restarted.
+ // When encrypted device is booting, we get EROFS since
+ // FUSE hasn't been created yet by init.
+ // In either case, continue without external storage.
+ } else {
+ ALOGE("Cannot continue without emulated storage");
+ RuntimeAbort(env);
+ }
+ }
+
+ SetGids(env, javaGids);
+
+ SetRLimits(env, javaRlimits);
+
+ int rc = setresgid(gid, gid, gid);
+ if (rc == -1) {
+ ALOGE("setresgid(%d) failed", gid);
+ RuntimeAbort(env);
+ }
+
+ rc = setresuid(uid, uid, uid);
+ if (rc == -1) {
+ ALOGE("setresuid(%d) failed", uid);
+ RuntimeAbort(env);
+ }
+
+#if defined(__linux__)
+ if (NeedsNoRandomizeWorkaround()) {
+ // Work around ARM kernel ASLR lossage (http://b/5817320).
+ int old_personality = personality(0xffffffff);
+ int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
+ if (new_personality == -1) {
+ ALOGW("personality(%d) failed", new_personality);
+ }
+ }
+#endif
+
+ SetCapabilities(env, permittedCapabilities, effectiveCapabilities);
+
+ SetSchedulerPolicy(env);
+
+#if defined(HAVE_ANDROID_OS)
+ { // NOLINT(whitespace/braces)
+ const char* se_info_c_str = NULL;
+ ScopedUtfChars* se_info = NULL;
+ if (java_se_info != NULL) {
+ se_info = new ScopedUtfChars(env, java_se_info);
+ se_info_c_str = se_info->c_str();
+ if (se_info_c_str == NULL) {
+ ALOGE("se_info_c_str == NULL");
+ RuntimeAbort(env);
+ }
+ }
+ const char* se_name_c_str = NULL;
+ ScopedUtfChars* se_name = NULL;
+ if (java_se_name != NULL) {
+ se_name = new ScopedUtfChars(env, java_se_name);
+ se_name_c_str = se_name->c_str();
+ if (se_name_c_str == NULL) {
+ ALOGE("se_name_c_str == NULL");
+ RuntimeAbort(env);
+ }
+ }
+ rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
+ if (rc == -1) {
+ ALOGE("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
+ is_system_server, se_info_c_str, se_name_c_str);
+ RuntimeAbort(env);
+ }
+
+ // Make it easier to debug audit logs by setting the main thread's name to the
+ // nice name rather than "app_process".
+ if (se_info_c_str == NULL && is_system_server) {
+ se_name_c_str = "system_server";
+ }
+ if (se_info_c_str != NULL) {
+ SetThreadName(se_name_c_str);
+ }
+
+ delete se_info;
+ delete se_name;
+ }
+#else
+ UNUSED(is_system_server);
+ UNUSED(java_se_info);
+ UNUSED(java_se_name);
+#endif
+
+ UnsetSigChldHandler();
+
+ env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, debug_flags);
+ if (env->ExceptionCheck()) {
+ ALOGE("Error calling post fork hooks.");
+ RuntimeAbort(env);
+ }
+ } else if (pid > 0) {
+ // the parent process
+ }
+ return pid;
+}
+} // anonymous namespace
+
+namespace android {
+
+static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
+ JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
+ jint debug_flags, jobjectArray rlimits,
+ jint mount_external, jstring se_info, jstring se_name,
+ jintArray fdsToClose) {
+ return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags,
+ rlimits, 0, 0, mount_external, se_info, se_name, false, fdsToClose);
+}
+
+static jint com_android_internal_os_Zygote_nativeForkSystemServer(
+ JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
+ jint debug_flags, jobjectArray rlimits, jlong permittedCapabilities,
+ jlong effectiveCapabilities) {
+ pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
+ debug_flags, rlimits,
+ permittedCapabilities, effectiveCapabilities,
+ MOUNT_EXTERNAL_NONE, NULL, NULL, true, NULL);
+ if (pid > 0) {
+ // The zygote process checks whether the child process has died or not.
+ ALOGI("System server process %d has been created", pid);
+ gSystemServerPid = pid;
+ // There is a slight window that the system server process has crashed
+ // but it went unnoticed because we haven't published its pid yet. So
+ // we recheck here just to make sure that all is well.
+ int status;
+ if (waitpid(pid, &status, WNOHANG) == pid) {
+ ALOGE("System server process %d has died. Restarting Zygote!", pid);
+ RuntimeAbort(env);
+ }
+ }
+ return pid;
+}
+
+static JNINativeMethod gMethods[] = {
+ { "nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;[I)I",
+ (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
+ { "nativeForkSystemServer", "(II[II[[IJJ)I",
+ (void *) com_android_internal_os_Zygote_nativeForkSystemServer }
+};
+
+int register_com_android_internal_os_Zygote(JNIEnv* env) {
+ gZygoteClass = (jclass) env->NewGlobalRef(env->FindClass(kZygoteClassName));
+ if (gZygoteClass == NULL) {
+ RuntimeAbort(env);
+ }
+ gCallPostForkChildHooks = env->GetStaticMethodID(gZygoteClass, "callPostForkChildHooks", "(I)V");
+
+ return AndroidRuntime::registerNativeMethods(env, "com/android/internal/os/Zygote",
+ gMethods, NELEM(gMethods));
+}
+} // namespace android
+
diff --git a/include/android_runtime/AndroidRuntime.h b/include/android_runtime/AndroidRuntime.h
index 649f4c3..3dfdb46 100644
--- a/include/android_runtime/AndroidRuntime.h
+++ b/include/android_runtime/AndroidRuntime.h
@@ -34,7 +34,7 @@
class AndroidRuntime
{
public:
- AndroidRuntime();
+ AndroidRuntime(char* argBlockStart, size_t argBlockSize);
virtual ~AndroidRuntime();
enum StartMode {
@@ -44,6 +44,8 @@
Tool,
};
+ void setArgv0(const char* argv0);
+
/**
* Register a set of methods in the specified class.
*/
@@ -53,8 +55,7 @@
/**
* Call a class's static main method with the given arguments,
*/
- status_t callMain(const char* className, jclass clazz, int argc,
- const char* const argv[]);
+ status_t callMain(const String8& className, jclass clazz, const Vector<String8>& args);
/**
* Find a class, with the input either of the form
@@ -64,7 +65,7 @@
int addVmArguments(int argc, const char* const argv[]);
- void start(const char *classname, const char* options);
+ void start(const char *classname, const Vector<String8>& options);
void exit(int code);
@@ -120,6 +121,8 @@
Vector<JavaVMOption> mOptions;
bool mExitWithoutCleanup;
+ char* const mArgBlockStart;
+ const size_t mArgBlockLength;
/* JNI JavaVM pointer */
static JavaVM* mJavaVM;
diff --git a/libs/androidfw/BackupData.cpp b/libs/androidfw/BackupData.cpp
index 2d0f755..bd9dc76 100644
--- a/libs/androidfw/BackupData.cpp
+++ b/libs/androidfw/BackupData.cpp
@@ -78,7 +78,7 @@
paddingSize = padding_extra(n);
if (paddingSize > 0) {
uint32_t padding = 0xbcbcbcbc;
- if (DEBUG) ALOGI("writing %d padding bytes for %d", paddingSize, n);
+ if (DEBUG) ALOGI("writing %zd padding bytes for %d", paddingSize, n);
amt = write(m_fd, &padding, paddingSize);
if (amt != paddingSize) {
m_status = errno;
@@ -112,7 +112,7 @@
k = key;
}
if (DEBUG) {
- ALOGD("Writing header: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(),
+ ALOGD("Writing header: prefix='%s' key='%s' dataSize=%zu", m_keyPrefix.string(),
key.string(), dataSize);
}
@@ -125,7 +125,7 @@
header.keyLen = tolel(keyLen);
header.dataSize = tolel(dataSize);
- if (DEBUG) ALOGI("writing entity header, %d bytes", sizeof(entity_header_v1));
+ if (DEBUG) ALOGI("writing entity header, %zu bytes", sizeof(entity_header_v1));
amt = write(m_fd, &header, sizeof(entity_header_v1));
if (amt != sizeof(entity_header_v1)) {
m_status = errno;
@@ -133,7 +133,7 @@
}
m_pos += amt;
- if (DEBUG) ALOGI("writing entity header key, %d bytes", keyLen+1);
+ if (DEBUG) ALOGI("writing entity header key, %zd bytes", keyLen+1);
amt = write(m_fd, k.string(), keyLen+1);
if (amt != keyLen+1) {
m_status = errno;
diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp
index ab837ad..52dce9f 100644
--- a/libs/androidfw/BackupHelpers.cpp
+++ b/libs/androidfw/BackupHelpers.cpp
@@ -639,7 +639,7 @@
// size header -- calc len in digits by actually rendering the number
// to a string - brute force but simple
- snprintf(sizeStr, sizeof(sizeStr), "%lld", s.st_size);
+ snprintf(sizeStr, sizeof(sizeStr), "%lld", (long long)s.st_size);
p += write_pax_header_entry(p, "size", sizeStr);
// fullname was generated above with the ustar paths
@@ -661,7 +661,7 @@
// [ 124 : 12 ] size of pax extended header data
memset(paxHeader + 124, 0, 12);
- snprintf(paxHeader + 124, 12, "%011o", p - paxData);
+ snprintf(paxHeader + 124, 12, "%011o", (unsigned int)(p - paxData));
// Checksum and write the pax block header
calc_tar_checksum(paxHeader);
@@ -681,7 +681,10 @@
if (!isdir) {
off64_t toWrite = s.st_size;
while (toWrite > 0) {
- size_t toRead = (toWrite < BUFSIZE) ? toWrite : BUFSIZE;
+ size_t toRead = toWrite;
+ if (toRead > BUFSIZE) {
+ toRead = BUFSIZE;
+ }
ssize_t nRead = read(fd, buf, toRead);
if (nRead < 0) {
err = errno;
@@ -1095,8 +1098,8 @@
if (name != filenames[i] || states[i].modTime_sec != state.modTime_sec
|| states[i].modTime_nsec != state.modTime_nsec || states[i].mode != state.mode
|| states[i].size != state.size || states[i].crc32 != states[i].crc32) {
- fprintf(stderr, "state %zu expected={%d/%d, 0x%08x, %04o, 0x%08x, %3d} '%s'\n"
- " actual={%d/%d, 0x%08x, %04o, 0x%08x, %3zu} '%s'\n", i,
+ fprintf(stderr, "state %zu expected={%d/%d, %04o, 0x%08x, 0x%08x, %3zu} '%s'\n"
+ " actual={%d/%d, %04o, 0x%08x, 0x%08x, %3d} '%s'\n", i,
states[i].modTime_sec, states[i].modTime_nsec, states[i].mode, states[i].size,
states[i].crc32, name.length(), filenames[i].string(),
state.modTime_sec, state.modTime_nsec, state.mode, state.size, state.crc32,
@@ -1194,7 +1197,7 @@
test_read_header_and_entity(BackupDataReader& reader, const char* str)
{
int err;
- int bufSize = strlen(str)+1;
+ size_t bufSize = strlen(str)+1;
char* buf = (char*)malloc(bufSize);
String8 string;
int cookie = 0x11111111;
@@ -1229,9 +1232,9 @@
err = EINVAL;
goto finished;
}
- if ((int)actualSize != bufSize) {
- fprintf(stderr, "ReadEntityHeader expected dataSize 0x%08x got 0x%08zx\n", bufSize,
- actualSize);
+ if (actualSize != bufSize) {
+ fprintf(stderr, "ReadEntityHeader expected dataSize %zu got %zu\n",
+ bufSize, actualSize);
err = EINVAL;
goto finished;
}
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 2b74a33..166863c 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -210,8 +210,8 @@
uint32_t offset = mHeader->freeOffset + padding;
uint32_t nextFreeOffset = offset + size;
if (nextFreeOffset > mSize) {
- ALOGW("Window is full: requested allocation %d bytes, "
- "free space %d bytes, window size %d bytes",
+ ALOGW("Window is full: requested allocation %zu bytes, "
+ "free space %zu bytes, window size %zu bytes",
size, freeSpace(), mSize);
return 0;
}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 652cd4a..1ffe665 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -5996,12 +5996,12 @@
uintptr_t esize = dtohs(ent->size);
if ((esize&0x3) != 0) {
- printf("NON-INTEGER ResTable_entry SIZE: 0x%x\n", esize);
+ printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize);
continue;
}
if ((thisOffset+esize) > typeSize) {
- printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+0x%x (size is 0x%x)\n",
- entriesStart, thisOffset, esize, typeSize);
+ printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n",
+ entriesStart, thisOffset, (void *)esize, typeSize);
continue;
}
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index b2148b0..442e9ba 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -36,6 +36,7 @@
}
Patch::~Patch() {
+ delete[] vertices;
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index 8a44604..2f2debc 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -119,6 +119,17 @@
void PatchCache::removeDeferred(Res_png_9patch* patch) {
Mutex::Autolock _l(mLock);
+
+ // Assert that patch is not already garbage
+ size_t count = mGarbage.size();
+ for (size_t i = 0; i < count; i++) {
+ if (patch == mGarbage[i]) {
+ patch = NULL;
+ break;
+ }
+ }
+ LOG_ALWAYS_FATAL_IF(patch == NULL);
+
mGarbage.push(patch);
}
@@ -143,8 +154,8 @@
for (size_t i = 0; i < patchesToRemove.size(); i++) {
const patch_pair_t& pair = patchesToRemove[i];
- // Add a new free block to the list
- const Patch* patch = pair.getSecond();
+ // Release the patch and mark the space in the free list
+ Patch* patch = pair.getSecond();
BufferBlock* block = new BufferBlock(patch->offset, patch->getSize());
block->next = mFreeBlocks;
mFreeBlocks = block;
@@ -152,6 +163,7 @@
mSize -= patch->getSize();
mCache.remove(*pair.getFirst());
+ delete patch;
}
#if DEBUG_PATCHES
@@ -216,6 +228,7 @@
} else {
mFreeBlocks = block->next;
}
+ delete block;
} else {
// Resize the block now that it's occupied
block->offset += size;
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index d276a29..77292bf 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -31,9 +31,9 @@
ALOGD("ResourceCache: cacheReport:");
for (size_t i = 0; i < mCache->size(); ++i) {
ResourceReference* ref = mCache->valueAt(i);
- ALOGD(" ResourceCache: mCache(%d): resource, ref = 0x%p, 0x%p",
+ ALOGD(" ResourceCache: mCache(%zu): resource, ref = 0x%p, 0x%p",
i, mCache->keyAt(i), mCache->valueAt(i));
- ALOGD(" ResourceCache: mCache(%d): refCount, recycled, destroyed, type = %d, %d, %d, %d",
+ ALOGD(" ResourceCache: mCache(%zu): refCount, recycled, destroyed, type = %d, %d, %d, %d",
i, ref->refCount, ref->recycled, ref->destroyed, ref->resourceType);
}
}
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index cc6d0cd..5bdb18a 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -108,7 +108,7 @@
* Returns the current clip in local coordinates. The clip rect is
* transformed by the inverse transform matrix.
*/
- const Rect& getLocalClip();
+ ANDROID_API const Rect& getLocalClip();
/**
* Resets the clip to the specified rect.
diff --git a/libs/usb/tests/AccessoryChat/accessorychat/accessorychat.c b/libs/usb/tests/AccessoryChat/accessorychat/accessorychat.c
index 06b477f..57c0320 100644
--- a/libs/usb/tests/AccessoryChat/accessorychat/accessorychat.c
+++ b/libs/usb/tests/AccessoryChat/accessorychat/accessorychat.c
@@ -32,7 +32,7 @@
struct usb_device *sDevice = NULL;
static void* read_thread(void* arg) {
- int endpoint = (int)arg;
+ int endpoint = (int)(uintptr_t)arg;
int ret = 0;
while (sDevice && ret >= 0) {
@@ -52,7 +52,7 @@
}
static void* write_thread(void* arg) {
- int endpoint = (int)arg;
+ int endpoint = (int)(uintptr_t)arg;
int ret = 0;
while (ret >= 0) {
@@ -136,11 +136,11 @@
}
if ((ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
- pthread_create(&th, NULL, read_thread, (void *)ep1->bEndpointAddress);
- pthread_create(&th, NULL, write_thread, (void *)ep2->bEndpointAddress);
+ pthread_create(&th, NULL, read_thread, (void *)(uintptr_t)ep1->bEndpointAddress);
+ pthread_create(&th, NULL, write_thread, (void *)(uintptr_t)ep2->bEndpointAddress);
} else {
- pthread_create(&th, NULL, read_thread, (void *)ep2->bEndpointAddress);
- pthread_create(&th, NULL, write_thread, (void *)ep1->bEndpointAddress);
+ pthread_create(&th, NULL, read_thread, (void *)(uintptr_t)ep2->bEndpointAddress);
+ pthread_create(&th, NULL, write_thread, (void *)(uintptr_t)ep1->bEndpointAddress);
}
} else {
printf("Found possible android device - attempting to switch to accessory mode\n");
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 543cb6c..854ee79 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -84,7 +84,7 @@
jbyteArray byteArrayObj = env->NewByteArray(size);
env->DeleteLocalRef(env->GetObjectClass(mDataSource));
env->DeleteLocalRef(env->GetObjectClass(byteArrayObj));
- ssize_t numread = env->CallIntMethod(mDataSource, mReadMethod, offset, byteArrayObj, size);
+ ssize_t numread = env->CallIntMethod(mDataSource, mReadMethod, offset, byteArrayObj, (jint)size);
env->GetByteArrayRegion(byteArrayObj, 0, size, (jbyte*) buffer);
env->DeleteLocalRef(byteArrayObj);
if (env->ExceptionCheck()) {
@@ -627,7 +627,7 @@
env->CallVoidMethod(
cryptoInfoObj,
gFields.cryptoInfoSetID,
- numSubSamples,
+ (jint)numSubSamples,
numBytesOfPlainDataObj,
numBytesOfEncryptedDataObj,
keyObj,
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 72ce3cc..8129c0d 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -17,11 +17,12 @@
#define LOG_TAG "MtpDatabaseJNI"
#include "utils/Log.h"
-#include <stdio.h>
#include <assert.h>
-#include <limits.h>
-#include <unistd.h>
#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
#include "jni.h"
#include "JNIHelp.h"
@@ -388,7 +389,7 @@
// release date is stored internally as just the year
if (property == MTP_PROPERTY_ORIGINAL_RELEASE_DATE) {
char date[20];
- snprintf(date, sizeof(date), "%04lld0101T000000", longValue);
+ snprintf(date, sizeof(date), "%04" PRId64 "0101T000000", longValue);
packet.putString(date);
goto out;
}
@@ -645,7 +646,7 @@
return result;
}
-MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty property) {
+MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty /*property*/) {
return -1;
}
@@ -1090,7 +1091,7 @@
}
static jstring
-android_mtp_MtpPropertyGroup_format_date_time(JNIEnv *env, jobject thiz, jlong seconds)
+android_mtp_MtpPropertyGroup_format_date_time(JNIEnv *env, jobject /*thiz*/, jlong seconds)
{
char date[20];
formatDateTime(seconds, date, sizeof(date));
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 6e34bbb..48ef9db 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -30,6 +30,7 @@
import android.content.res.ObbInfo;
import android.content.res.ObbScanner;
import android.net.Uri;
+import android.os.Build;
import android.os.Environment;
import android.os.Environment.UserEnvironment;
import android.os.FileUtils;
@@ -39,10 +40,8 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StatFs;
-import android.os.SystemClock;
import android.provider.Settings;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.util.Slog;
import com.android.internal.app.IMediaContainerService;
@@ -343,11 +342,13 @@
// The .apk file
String codePath = packageURI.getPath();
File codeFile = new File(codePath);
+ NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codePath);
+ final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS);
// Calculate size of container needed to hold base APK.
final int sizeMb;
try {
- sizeMb = calculateContainerSize(codeFile, isForwardLocked);
+ sizeMb = calculateContainerSize(handle, codeFile, abi, isForwardLocked);
} catch (IOException e) {
Slog.w(TAG, "Problem when trying to copy " + codeFile.getPath());
return null;
@@ -410,7 +411,14 @@
final File sharedLibraryDir = new File(newCachePath, LIB_DIR_NAME);
if (sharedLibraryDir.mkdir()) {
- int ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(codeFile, sharedLibraryDir);
+ int ret = PackageManager.INSTALL_SUCCEEDED;
+ if (abi >= 0) {
+ ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
+ sharedLibraryDir, Build.SUPPORTED_ABIS[abi]);
+ } else if (abi != PackageManager.NO_NATIVE_LIBRARIES) {
+ ret = abi;
+ }
+
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Could not copy native libraries to " + sharedLibraryDir.getPath());
PackageHelper.destroySdDir(newCid);
@@ -824,6 +832,17 @@
return availSdMb > sizeMb;
}
+ private int calculateContainerSize(File apkFile, boolean forwardLocked) throws IOException {
+ NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(apkFile);
+ final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS);
+
+ try {
+ return calculateContainerSize(handle, apkFile, abi, forwardLocked);
+ } finally {
+ handle.close();
+ }
+ }
+
/**
* Calculate the container size for an APK. Takes into account the
*
@@ -831,7 +850,8 @@
* @return size in megabytes (2^20 bytes)
* @throws IOException when there is a problem reading the file
*/
- private int calculateContainerSize(File apkFile, boolean forwardLocked) throws IOException {
+ private int calculateContainerSize(NativeLibraryHelper.ApkHandle apkHandle,
+ File apkFile, int abiIndex, boolean forwardLocked) throws IOException {
// Calculate size of container needed to hold base APK.
long sizeBytes = apkFile.length();
if (sizeBytes == 0 && !apkFile.exists()) {
@@ -840,7 +860,10 @@
// Check all the native files that need to be copied and add that to the
// container size.
- sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(apkFile);
+ if (abiIndex >= 0) {
+ sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(apkHandle,
+ Build.SUPPORTED_ABIS[abiIndex]);
+ }
if (forwardLocked) {
sizeBytes += PackageHelper.extractPublicFiles(apkFile.getPath(), null);
diff --git a/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm b/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
new file mode 100644
index 0000000..2a95cfe
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
@@ -0,0 +1,319 @@
+# Copyright (C) 2013 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.
+
+#
+# Arabic (US-101 keys) keyboard layout.
+#
+
+type OVERLAY
+
+### ROW 1
+
+key GRAVE {
+ label: '`'
+ base, capslock: '\u0630'
+ shift: '\u0651'
+}
+
+key 1 {
+ label: '1'
+ base: '\u0661'
+ shift: '!'
+ capslock: '1'
+}
+
+key 2 {
+ label: '2'
+ base: '\u0662'
+ shift: '@'
+ capslock: '2'
+}
+
+key 3 {
+ label: '3'
+ base: '\u0663'
+ shift: '#'
+ capslock: '3'
+}
+
+key 4 {
+ label: '4'
+ base: '\u0664'
+ shift: '$'
+ capslock: '4'
+}
+
+key 5 {
+ label: '5'
+ base: '\u0665'
+ shift: '%'
+ capslock: '5'
+}
+
+key 6 {
+ label: '6'
+ base: '\u0666'
+ shift: '^'
+ capslock: '6'
+}
+
+key 7 {
+ label: '7'
+ base: '\u0667'
+ shift: '&'
+ capslock: '7'
+}
+
+key 8 {
+ label: '8'
+ base: '\u0668'
+ shift: '*'
+ capslock: '8'
+}
+
+key 9 {
+ label: '9'
+ base: '\u0669'
+ shift: '('
+ capslock: '9'
+}
+
+key 0 {
+ label: '0'
+ base: '\u0660'
+ shift: ')'
+ capslock: '0'
+}
+
+key MINUS {
+ label: '-'
+ base, capslock: '-'
+ shift: '_'
+}
+
+key EQUALS {
+ label: '='
+ base, capslock: '='
+ shift: '+'
+}
+
+### ROW 2
+
+key Q {
+ label: 'Q'
+ base, capslock: '\u0636'
+ shift: '\u064e'
+}
+
+key W {
+ label: 'W'
+ base, capslock: '\u0635'
+ shift: '\u064b'
+}
+
+key E {
+ label: 'E'
+ base, capslock: '\u062b'
+ shift: '\u064f'
+}
+
+key R {
+ label: 'R'
+ base, capslock: '\u0642'
+ shift: '\u064c'
+}
+
+key T {
+ label: 'T'
+ base, capslock: '\u0641'
+ shift: '\ufef9'
+}
+
+key Y {
+ label: 'Y'
+ base, capslock: '\u063a'
+ shift: '\u0625'
+}
+
+key U {
+ label: 'U'
+ base, capslock: '\u0639'
+ shift: '\u2018'
+}
+
+key I {
+ label: 'I'
+ base, capslock: '\u0647'
+ shift: '\u00f7'
+}
+
+key O {
+ label: 'O'
+ base, capslock: '\u062e'
+ shift: '\u00d7'
+}
+
+key P {
+ label: 'P'
+ base, capslock: '\u062d'
+ shift: '\u061b'
+}
+
+key LEFT_BRACKET {
+ label: '['
+ base, capslock: '\u062c'
+ shift: '<'
+}
+
+key RIGHT_BRACKET {
+ label: ']'
+ base, capslock: '\u062f'
+ shift: '>'
+}
+
+key BACKSLASH {
+ label: '\\'
+ base, capslock: '\\'
+ shift: '|'
+}
+
+### ROW 3
+
+key A {
+ label: 'A'
+ base, capslock: '\u0634'
+ shift: '\u0650'
+}
+
+key S {
+ label: 'S'
+ base, capslock: '\u0633'
+ shift: '\u064d'
+}
+
+key D {
+ label: 'D'
+ base, capslock: '\u064a'
+ shift: ']'
+}
+
+key F {
+ label: 'F'
+ base, capslock: '\u0628'
+ shift: '['
+}
+
+key G {
+ label: 'G'
+ base, capslock: '\u0644'
+ shift: '\ufef7'
+}
+
+key H {
+ label: 'H'
+ base, capslock: '\u0627'
+ shift: '\u0623'
+}
+
+key J {
+ label: 'J'
+ base, capslock: '\u062a'
+ shift: '\u0640'
+}
+
+key K {
+ label: 'K'
+ base, capslock: '\u0646'
+ shift: '\u060c'
+}
+
+key L {
+ label: 'L'
+ base, capslock: '\u0645'
+ shift: '/'
+}
+
+key SEMICOLON {
+ label: ';'
+ base, capslock: '\u0643'
+ shift: ':'
+}
+
+key APOSTROPHE {
+ label: '\''
+ base, capslock: '\u0637'
+ shift: '"'
+}
+
+### ROW 4
+
+key Z {
+ label: 'Z'
+ base, capslock: '\u0626'
+ shift: '~'
+}
+
+key X {
+ label: 'X'
+ base, capslock: '\u0621'
+ shift: '\u0652'
+}
+
+key C {
+ label: 'C'
+ base, capslock: '\u0624'
+ shift: '}'
+}
+
+key V {
+ label: 'V'
+ base, capslock: '\u0631'
+ shift: '{'
+}
+
+key B {
+ label: 'B'
+ base, capslock: '\ufefb'
+ shift: '\ufef5'
+}
+
+key N {
+ label: 'N'
+ base, capslock: '\u0649'
+ shift: '\u0622'
+}
+
+key M {
+ label: 'M'
+ base, capslock: '\u0629'
+ shift: '\u2019'
+}
+
+key COMMA {
+ label: ','
+ base, capslock: '\u0648'
+ shift: ','
+}
+
+key PERIOD {
+ label: '.'
+ base, capslock: '\u0632'
+ shift: '.'
+}
+
+key SLASH {
+ label: '/'
+ base, capslock: '\u0638'
+ shift: '\u061f'
+}
diff --git a/packages/InputDevices/res/raw/keyboard_layout_greek.kcm b/packages/InputDevices/res/raw/keyboard_layout_greek.kcm
new file mode 100644
index 0000000..a7684e1
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_greek.kcm
@@ -0,0 +1,338 @@
+# Copyright (C) 2013 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.
+
+#
+# Greek (EU based) keyboard layout.
+#
+
+type OVERLAY
+
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+ label: '`'
+ base, capslock: '`'
+ shift: '~'
+}
+
+key 1 {
+ label: '1'
+ base, capslock: '1'
+ shift: '!'
+}
+
+key 2 {
+ label: '2'
+ base, capslock: '2'
+ shift: '@'
+ ralt: '\u00b2'
+}
+
+key 3 {
+ label: '3'
+ base, capslock: '3'
+ shift: '#'
+ ralt: '\u00b3'
+}
+
+key 4 {
+ label: '4'
+ base, capslock: '4'
+ shift: '$'
+ ralt: '\u00a3'
+}
+
+key 5 {
+ label: '5'
+ base, capslock: '5'
+ shift: '%'
+ ralt: '\u00a7'
+}
+
+key 6 {
+ label: '6'
+ base, capslock: '6'
+ shift: '^'
+ ralt: '\u00b6'
+}
+
+key 7 {
+ label: '7'
+ base, capslock: '7'
+ shift: '&'
+}
+
+key 8 {
+ label: '8'
+ base, capslock: '8'
+ shift: '*'
+ ralt: '\u00a4'
+}
+
+key 9 {
+ label: '9'
+ base, capslock: '9'
+ shift: '('
+ ralt: '\u00a6'
+}
+
+key 0 {
+ label: '0'
+ base, capslock: '0'
+ shift: ')'
+ ralt: '\u00b0'
+}
+
+key MINUS {
+ label: '-'
+ base, capslock: '-'
+ shift: '_'
+ ralt: '\u00b1'
+}
+
+key EQUALS {
+ label: '='
+ base, capslock: '='
+ shift: '+'
+ ralt: '\u00bd'
+}
+
+### ROW 2
+
+key Q {
+ label: 'Q'
+ base, capslock: ';'
+ shift: ':'
+}
+
+key W {
+ label: 'W'
+ base, capslock: '\u03c2'
+ shift: '\u0385'
+}
+
+key E {
+ label: 'E'
+ base: '\u03b5'
+ shift, capslock: '\u0395'
+ ralt: '\u20ac'
+}
+
+key R {
+ label: 'R'
+ base: '\u03c1'
+ shift, capslock: '\u03a1'
+ ralt: '\u00ae'
+}
+
+key T {
+ label: 'T'
+ base: '\u03c4'
+ shift, capslock: '\u03a4'
+}
+
+key Y {
+ label: 'Y'
+ base: '\u03c5'
+ shift, capslock: '\u03a5'
+ ralt: '\u00a5'
+}
+
+key U {
+ label: 'U'
+ base: '\u03b8'
+ shift, capslock: '\u0398'
+}
+
+key I {
+ label: 'I'
+ base: '\u03b9'
+ shift, capslock: '\u0399'
+}
+
+key O {
+ label: 'O'
+ base: '\u03bf'
+ shift, capslock: '\u039f'
+}
+
+key P {
+ label: 'P'
+ base: '\u03c0'
+ shift, capslock: '\u03a0'
+}
+
+key LEFT_BRACKET {
+ label: '['
+ base, capslock: '['
+ shift: '{'
+ ralt: '\u00ab'
+}
+
+key RIGHT_BRACKET {
+ label: ']'
+ base, capslock: ']'
+ shift: '}'
+ ralt: '\u00bb'
+}
+
+### ROW 3
+
+key A {
+ label: 'A'
+ base: '\u03b1'
+ shift, capslock: '\u0391'
+}
+
+key S {
+ label: 'S'
+ base: '\u03c3'
+ shift, capslock: '\u03a3'
+}
+
+key D {
+ label: 'D'
+ base: '\u03b4'
+ shift, capslock: '\u0394'
+}
+
+key F {
+ label: 'F'
+ base: '\u03c6'
+ shift, capslock: '\u03a6'
+}
+
+key G {
+ label: 'G'
+ base: '\u03b3'
+ shift, capslock: '\u0393'
+}
+
+key H {
+ label: 'H'
+ base: '\u03b7'
+ shift, capslock: '\u0397'
+}
+
+key J {
+ label: 'J'
+ base: '\u03be'
+ shift, capslock: '\u039e'
+}
+
+key K {
+ label: 'K'
+ base: '\u03ba'
+ shift, capslock: '\u039a'
+}
+
+key L {
+ label: 'L'
+ base: '\u03bb'
+ shift, capslock: '\u039b'
+}
+
+key SEMICOLON {
+ label: ';'
+ base, capslock: '\u0301'
+#should be \u0384 (greek tonos)
+ shift: '\u0308'
+ ralt: '\u0385'
+}
+
+key APOSTROPHE {
+ label: '\''
+ base, capslock: '\''
+ shift: '"'
+}
+
+key BACKSLASH {
+ label: '\\'
+ base, capslock: '\\'
+ shift: '|'
+ ralt: '\u00ac'
+}
+
+### ROW 4
+
+key PLUS {
+ label: '<'
+ base, capslock: '<'
+ shift: '>'
+ ralt: '\\'
+ shift+ralt: '|'
+}
+
+key Z {
+ label: 'Z'
+ base: '\u03b6'
+ shift, capslock: '\u0396'
+}
+
+key X {
+ label: 'X'
+ base: '\u03c7'
+ shift, capslock: '\u03a7'
+}
+
+key C {
+ label: 'C'
+ base: '\u03c8'
+ shift, capslock: '\u03a8'
+ ralt: '\u00a9'
+}
+
+key V {
+ label: 'V'
+ base: '\u03c9'
+ shift, capslock: '\u03a9'
+}
+
+key B {
+ label: 'B'
+ base: '\u03b2'
+ shift, capslock: '\u0392'
+}
+
+key N {
+ label: 'N'
+ base: '\u03bd'
+ shift, capslock: '\u039d'
+}
+
+key M {
+ label: 'M'
+ base: '\u03bc'
+ shift, capslock: '\u039c'
+}
+
+key COMMA {
+ label: ','
+ base, capslock: ','
+ shift: '<'
+}
+
+key PERIOD {
+ label: '.'
+ base, capslock: '.'
+ shift: '>'
+}
+
+key SLASH {
+ label: '/'
+ base, capslock: '/'
+ shift: '?'
+}
diff --git a/packages/InputDevices/res/raw/keyboard_layout_hebrew.kcm b/packages/InputDevices/res/raw/keyboard_layout_hebrew.kcm
new file mode 100644
index 0000000..cd3a4b9
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_hebrew.kcm
@@ -0,0 +1,341 @@
+# Copyright (C) 2013 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.
+
+#
+# Hebrew (based EU) keyboard layout.
+#
+
+type OVERLAY
+
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+ label: '`'
+ base: ';'
+ shift: '~'
+ shift+capslock: '\u05b0'
+}
+
+key 1 {
+ label: '1'
+ base: '1'
+ shift: '!'
+ shift+capslock: '\u05b1'
+}
+
+key 2 {
+ label: '2'
+ base: '2'
+ shift: '@'
+ shift+capslock: '\u05b2'
+}
+
+key 3 {
+ label: '3'
+ base: '3'
+ shift: '#'
+ shift+capslock: '\u05b3'
+}
+
+key 4 {
+ label: '4'
+ base: '4'
+ shift: '$'
+ ralt: '\u20aa'
+ shift+capslock: '\u05b4'
+}
+
+key 5 {
+ label: '5'
+ base: '5'
+ shift: '%'
+ shift+capslock: '\u05b5'
+}
+
+key 6 {
+ label: '6'
+ base: '6'
+ shift: '^'
+ shift+capslock: '\u05b6'
+}
+
+key 7 {
+ label: '7'
+ base: '7'
+ shift: '&'
+ shift+capslock: '\u05b7'
+}
+
+key 8 {
+ label: '8'
+ base: '8'
+ shift: '*'
+ shift+capslock: '\u05b8'
+}
+
+key 9 {
+ label: '9'
+ base: '9'
+ shift: '('
+ shift+capslock: '\u05c2'
+}
+
+key 0 {
+ label: '0'
+ base: '0'
+ shift: ')'
+ shift+capslock: '\u05c1'
+}
+
+key MINUS {
+ label: '-'
+ base: '-'
+ shift: '_'
+ ralt: '\u05bf'
+ shift+capslock: '\u05b9'
+}
+
+key EQUALS {
+ label: '='
+ base: '='
+ shift: '+'
+ shift+capslock: '\u05bc'
+}
+
+### ROW 2
+
+key Q {
+ label: 'Q'
+ base: '/'
+ shift, capslock: 'Q'
+}
+
+key W {
+ label: 'W'
+ base: '\u0027'
+ shift, capslock: 'W'
+}
+
+key E {
+ label: 'E'
+ base: '\u05e7'
+ shift, capslock: 'E'
+ ralt: '\u20ac'
+}
+
+key R {
+ label: 'R'
+ base: '\u05e8'
+ shift, capslock: 'R'
+}
+
+key T {
+ label: 'T'
+ base: '\u05d0'
+ shift, capslock: 'T'
+}
+
+key Y {
+ label: 'Y'
+ base: '\u05d8'
+ shift, capslock: 'Y'
+}
+
+key U {
+ label: 'U'
+ base: '\u05d5'
+ shift, capslock: 'U'
+ ralt: '\u05f0'
+}
+
+key I {
+ label: 'I'
+ base: '\u05df'
+ shift, capslock: 'I'
+}
+
+key O {
+ label: 'O'
+ base: '\u05dd'
+ shift, capslock: 'O'
+}
+
+key P {
+ label: 'P'
+ base: '\u05e4'
+ shift, capslock: 'P'
+}
+
+key LEFT_BRACKET {
+ label: '['
+ base, capslock: '['
+ shift: '{'
+}
+
+key RIGHT_BRACKET {
+ label: ']'
+ base, capslock: ']'
+ shift: '}'
+}
+
+### ROW 3
+
+key A {
+ label: 'A'
+ base: '\u05e9'
+ shift, capslock: 'A'
+}
+
+key S {
+ label: 'S'
+ base: '\u05d3'
+ shift, capslock: 'S'
+}
+
+key D {
+ label: 'D'
+ base: '\u05d2'
+ shift, capslock: 'D'
+}
+
+key F {
+ label: 'F'
+ base: '\u05db'
+ shift, capslock: 'F'
+}
+
+key G {
+ label: 'G'
+ base: '\u05e2'
+ shift, capslock: 'G'
+}
+
+key H {
+ label: 'H'
+ base: '\u05d9'
+ shift, capslock: 'H'
+ ralt: '\u05f2'
+}
+
+key J {
+ label: 'J'
+ base: '\u05d7'
+ shift, capslock: 'J'
+ ralt: '\u05f1'
+}
+
+key K {
+ label: 'K'
+ base: '\u05dc'
+ shift, capslock: 'K'
+}
+
+key L {
+ label: 'L'
+ base: '\u05da'
+ shift, capslock: 'L'
+}
+
+key SEMICOLON {
+ label: ';'
+ base: '\u05e3'
+ shift: ':'
+ capslock: ';'
+}
+
+key APOSTROPHE {
+ label: '\''
+ base: ','
+ shift: '"'
+ capslock: '\''
+}
+
+key BACKSLASH {
+ label: '\\'
+ base: '\\'
+ shift: '|'
+}
+
+### ROW 4
+
+key PLUS {
+ label: '\\'
+ base, capslock: '\\'
+ shift: '|'
+}
+
+key Z {
+ label: 'Z'
+ base: '\u05d6'
+ shift, capslock: 'Z'
+}
+
+key X {
+ label: 'X'
+ base: '\u05e1'
+ shift, capslock: 'X'
+}
+
+key C {
+ label: 'C'
+ base: '\u05d1'
+ shift, capslock: 'C'
+}
+
+key V {
+ label: 'V'
+ base: '\u05d4'
+ shift, capslock: 'V'
+}
+
+key B {
+ label: 'B'
+ base: '\u05e0'
+ shift, capslock: 'B'
+}
+
+key N {
+ label: 'N'
+ base: '\u05de'
+ shift, capslock: 'N'
+}
+
+key M {
+ label: 'M'
+ base: '\u05e6'
+ shift, capslock: 'M'
+}
+
+key COMMA {
+ label: ','
+ base: '\u05ea'
+ shift: '<'
+ capslock: ','
+}
+
+key PERIOD {
+ label: '.'
+ base: '\u05e5'
+ shift: '>'
+ capslock: '.'
+}
+
+key SLASH {
+ label: '/'
+ base: '.'
+ shift: '?'
+ capslock: '/'
+}
diff --git a/packages/InputDevices/res/raw/keyboard_layout_lithuanian.kcm b/packages/InputDevices/res/raw/keyboard_layout_lithuanian.kcm
new file mode 100644
index 0000000..72ca333
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_lithuanian.kcm
@@ -0,0 +1,338 @@
+# Copyright (C) 2013 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.
+
+#
+# Lithuanian (EU based) keyboard layout.
+#
+
+type OVERLAY
+
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+ label: '`'
+ base: '`'
+ shift: '~'
+}
+
+key 1 {
+ label: '1'
+ base: '\u0105'
+ shift, capslock: '\u0104'
+ ralt: '1'
+ shift+ralt: '!'
+}
+
+key 2 {
+ label: '2'
+ base: '\u010d'
+ shift, capslock: '\u010c'
+ ralt: '2'
+ shift+ralt: '@'
+}
+
+key 3 {
+ label: '3'
+ base: '\u0119'
+ shift, capslock: '\u0118'
+ ralt: '3'
+ shift+ralt: '#'
+}
+
+key 4 {
+ label: '4'
+ base: '\u0117'
+ shift, capslock: '\u0116'
+ ralt: '4'
+ shift+ralt: '$'
+}
+
+key 5 {
+ label: '5'
+ base: '\u012f'
+ shift, capslock: '\u012e'
+ ralt: '5'
+ shift+ralt: '%'
+}
+
+key 6 {
+ label: '6'
+ base: '\u0161'
+ shift, capslock: '\u0160'
+ ralt: '6'
+ shift+ralt: '\u0302'
+}
+
+key 7 {
+ label: '7'
+ base: '\u0173'
+ shift, capslock: '\u0172'
+ ralt: '7'
+ shift+ralt: '&'
+}
+
+key 8 {
+ label: '8'
+ base: '\u016b'
+ shift, capslock: '\u016a'
+ ralt: '8'
+ shift+ralt: '*'
+}
+
+key 9 {
+ label: '9'
+ base: '9'
+ shift: '('
+ ralt: '9'
+}
+
+key 0 {
+ label: '0'
+ base: '0'
+ shift: ')'
+ ralt: '0'
+}
+
+key MINUS {
+ label: '-'
+ base: '-'
+ shift: '_'
+}
+
+key EQUALS {
+ label: '='
+ base: '\u017e'
+ shift, capslock: '\u017d'
+ ralt: '='
+ shift+ralt: '+'
+}
+
+### ROW 2
+
+key Q {
+ label: 'Q'
+ base: 'q'
+ shift, capslock: 'Q'
+}
+
+key W {
+ label: 'W'
+ base: 'w'
+ shift, capslock: 'W'
+}
+
+key E {
+ label: 'E'
+ base: 'e'
+ shift, capslock: 'E'
+ ralt: '\u20ac'
+}
+
+key R {
+ label: 'R'
+ base: 'r'
+ shift, capslock: 'R'
+}
+
+key T {
+ label: 'T'
+ base: 't'
+ shift, capslock: 'T'
+}
+
+key Y {
+ label: 'Y'
+ base: 'y'
+ shift, capslock: 'Y'
+}
+
+key U {
+ label: 'U'
+ base: 'u'
+ shift, capslock: 'U'
+}
+
+key I {
+ label: 'I'
+ base: 'i'
+ shift, capslock: 'I'
+}
+
+key O {
+ label: 'O'
+ base: 'o'
+ shift, capslock: 'O'
+}
+
+key P {
+ label: 'P'
+ base: 'p'
+ shift, capslock: 'P'
+}
+
+key LEFT_BRACKET {
+ label: '['
+ base: '['
+ shift: '{'
+}
+
+key RIGHT_BRACKET {
+ label: ']'
+ base: ']'
+ shift: '}'
+}
+
+### ROW 3
+
+key A {
+ label: 'A'
+ base: 'a'
+ shift, capslock: 'A'
+}
+
+key S {
+ label: 'S'
+ base: 's'
+ shift, capslock: 'S'
+}
+
+key D {
+ label: 'D'
+ base: 'd'
+ shift, capslock: 'D'
+}
+
+key F {
+ label: 'F'
+ base: 'f'
+ shift, capslock: 'F'
+}
+
+key G {
+ label: 'G'
+ base: 'g'
+ shift, capslock: 'G'
+}
+
+key H {
+ label: 'H'
+ base: 'h'
+ shift, capslock: 'H'
+}
+
+key J {
+ label: 'J'
+ base: 'j'
+ shift, capslock: 'J'
+}
+
+key K {
+ label: 'K'
+ base: 'k'
+ shift, capslock: 'K'
+}
+
+key L {
+ label: 'L'
+ base: 'l'
+ shift, capslock: 'L'
+}
+
+key SEMICOLON {
+ label: ';'
+ base: ';'
+ shift: ':'
+}
+
+key APOSTROPHE {
+ label: '\''
+ base: '\''
+ shift: '"'
+}
+
+key BACKSLASH {
+ label: '\\'
+ base: '\\'
+ shift: '|'
+}
+
+### ROW 4
+
+key PLUS {
+ label: '\\'
+ base: '\\'
+ shift: '|'
+}
+
+key Z {
+ label: 'Z'
+ base: 'z'
+ shift, capslock: 'Z'
+}
+
+key X {
+ label: 'X'
+ base: 'x'
+ shift, capslock: 'X'
+}
+
+key C {
+ label: 'C'
+ base: 'c'
+ shift, capslock: 'C'
+}
+
+key V {
+ label: 'V'
+ base: 'v'
+ shift, capslock: 'V'
+}
+
+key B {
+ label: 'B'
+ base: 'b'
+ shift, capslock: 'B'
+}
+
+key N {
+ label: 'N'
+ base: 'n'
+ shift, capslock: 'N'
+}
+
+key M {
+ label: 'M'
+ base: 'm'
+ shift, capslock: 'M'
+}
+
+key COMMA {
+ label: ','
+ base: ','
+ shift: '<'
+}
+
+key PERIOD {
+ label: '.'
+ base: '.'
+ shift: '>'
+}
+
+key SLASH {
+ label: '/'
+ base: '/'
+ shift: '?'
+}
diff --git a/packages/InputDevices/res/raw/keyboard_layout_spanish_latin.kcm b/packages/InputDevices/res/raw/keyboard_layout_spanish_latin.kcm
new file mode 100644
index 0000000..16eb53f
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_spanish_latin.kcm
@@ -0,0 +1,325 @@
+# Copyright (C) 2013 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.
+
+#
+# Spanish (Latin) (EU based) keyboard layout.
+#
+
+type OVERLAY
+
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+ label: '|'
+ base: '|'
+ shift: '\u00ba'
+ ralt: '\u00ac'
+}
+
+key 1 {
+ label: '1'
+ base: '1'
+ shift: '!'
+}
+
+key 2 {
+ label: '2'
+ base: '2'
+ shift: '"'
+}
+
+key 3 {
+ label: '3'
+ base: '3'
+ shift: '#'
+}
+
+key 4 {
+ label: '4'
+ base: '4'
+ shift: '$'
+
+}
+
+key 5 {
+ label: '5'
+ base: '5'
+ shift: '%'
+}
+
+key 6 {
+ label: '6'
+ base: '6'
+ shift: '&'
+}
+
+key 7 {
+ label: '7'
+ base: '7'
+ shift: '/'
+}
+
+key 8 {
+ label: '8'
+ base: '8'
+ shift: '('
+}
+
+key 9 {
+ label: '9'
+ base: '9'
+ shift: ')'
+}
+
+key 0 {
+ label: '0'
+ base: '0'
+ shift: '='
+}
+
+key MINUS {
+ label: '\''
+ base: '\''
+ shift: '?'
+ ralt: '\\'
+}
+
+key EQUALS {
+ label: '\u00bf'
+ base: '\u00bf'
+ shift: '\u00a1'
+}
+
+### ROW 2
+
+key Q {
+ label: 'Q'
+ base: 'q'
+ shift, capslock: 'Q'
+ ralt: '@'
+}
+
+key W {
+ label: 'W'
+ base: 'w'
+ shift, capslock: 'W'
+}
+
+key E {
+ label: 'E'
+ base: 'e'
+ shift, capslock: 'E'
+}
+
+key R {
+ label: 'R'
+ base: 'r'
+ shift, capslock: 'R'
+}
+
+key T {
+ label: 'T'
+ base: 't'
+ shift, capslock: 'T'
+}
+
+key Y {
+ label: 'Y'
+ base: 'y'
+ shift, capslock: 'Y'
+}
+
+key U {
+ label: 'U'
+ base: 'u'
+ shift, capslock: 'U'
+}
+
+key I {
+ label: 'I'
+ base: 'i'
+ shift, capslock: 'I'
+}
+
+key O {
+ label: 'O'
+ base: 'o'
+ shift, capslock: 'O'
+}
+
+key P {
+ label: 'P'
+ base: 'p'
+ shift, capslock: 'P'
+}
+
+key LEFT_BRACKET {
+ label: '\u00b4'
+ base: '\u0301'
+ shift: '\u0308'
+ ralt: '['
+}
+
+key RIGHT_BRACKET {
+ label: '+'
+ base: '+'
+ shift: '*'
+ ralt: '\u0303'
+}
+
+### ROW 3
+
+key A {
+ label: 'A'
+ base: 'a'
+ shift, capslock: 'A'
+}
+
+key S {
+ label: 'S'
+ base: 's'
+ shift, capslock: 'S'
+}
+
+key D {
+ label: 'D'
+ base: 'd'
+ shift, capslock: 'D'
+}
+
+key F {
+ label: 'F'
+ base: 'f'
+ shift, capslock: 'F'
+}
+
+key G {
+ label: 'G'
+ base: 'g'
+ shift, capslock: 'G'
+}
+
+key H {
+ label: 'H'
+ base: 'h'
+ shift, capslock: 'H'
+}
+
+key J {
+ label: 'J'
+ base: 'j'
+ shift, capslock: 'J'
+}
+
+key K {
+ label: 'K'
+ base: 'k'
+ shift, capslock: 'K'
+}
+
+key L {
+ label: 'L'
+ base: 'l'
+ shift, capslock: 'L'
+}
+
+key SEMICOLON {
+ label: '\u00d1'
+ base: '\u00f1'
+ shift, capslock: '\u00d1'
+}
+
+key APOSTROPHE {
+ label: '{'
+ base: '{'
+ shift: '['
+ ralt: '\u0302'
+}
+
+key BACKSLASH {
+ label: '}'
+ base: '}'
+ shift: ']'
+ ralt: '\u0300'
+}
+
+### ROW 4
+
+key PLUS {
+ label: '<'
+ base: '<'
+ shift: '>'
+}
+
+key Z {
+ label: 'Z'
+ base: 'z'
+ shift, capslock: 'Z'
+}
+
+key X {
+ label: 'X'
+ base: 'x'
+ shift, capslock: 'X'
+}
+
+key C {
+ label: 'C'
+ base: 'c'
+ shift, capslock: 'C'
+}
+
+key V {
+ label: 'V'
+ base: 'v'
+ shift, capslock: 'V'
+}
+
+key B {
+ label: 'B'
+ base: 'b'
+ shift, capslock: 'B'
+}
+
+key N {
+ label: 'N'
+ base: 'n'
+ shift, capslock: 'N'
+}
+
+key M {
+ label: 'M'
+ base: 'm'
+ shift, capslock: 'M'
+}
+
+key COMMA {
+ label: ','
+ base: ','
+ shift: ';'
+}
+
+key PERIOD {
+ label: '.'
+ base: '.'
+ shift: ':'
+}
+
+key SLASH {
+ label: '-'
+ base: '-'
+ shift: '_'
+}
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index 54c18f1..6239336 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -98,4 +98,19 @@
<!-- Ukrainian keyboard layout label. [CHAR LIMIT=35] -->
<string name="keyboard_layout_ukrainian">Ukrainian</string>
+
+ <!-- Arabic keyboard layout label. [CHAR LIMIT=35] -->
+ <string name="keyboard_layout_arabic">Arabic</string>
+
+ <!-- Greek keyboard layout label. [CHAR LIMIT=35] -->
+ <string name="keyboard_layout_greek">Greek</string>
+
+ <!-- Hebrew keyboard layout label. [CHAR LIMIT=35] -->
+ <string name="keyboard_layout_hebrew">Hebrew</string>
+
+ <!-- Lithuanian keyboard layout label. [CHAR LIMIT=35] -->
+ <string name="keyboard_layout_lithuanian">Lithuanian</string>
+
+ <!-- Spanish (Latin) keyboard layout label. [CHAR LIMIT=35] -->
+ <string name="keyboard_layout_spanish_latin">Spanish (Latin)</string>
</resources>
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index 1f48a36..dc1db0b 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -123,4 +123,24 @@
<keyboard-layout android:name="keyboard_layout_ukrainian"
android:label="@string/keyboard_layout_ukrainian"
android:keyboardLayout="@raw/keyboard_layout_ukrainian" />
+
+ <keyboard-layout android:name="keyboard_layout_arabic"
+ android:label="@string/keyboard_layout_arabic"
+ android:keyboardLayout="@raw/keyboard_layout_arabic" />
+
+ <keyboard-layout android:name="keyboard_layout_greek"
+ android:label="@string/keyboard_layout_greek"
+ android:keyboardLayout="@raw/keyboard_layout_greek" />
+
+ <keyboard-layout android:name="keyboard_layout_hebrew"
+ android:label="@string/keyboard_layout_hebrew"
+ android:keyboardLayout="@raw/keyboard_layout_hebrew" />
+
+ <keyboard-layout android:name="keyboard_layout_lithuanian"
+ android:label="@string/keyboard_layout_lithuanian"
+ android:keyboardLayout="@raw/keyboard_layout_lithuanian" />
+
+ <keyboard-layout android:name="keyboard_layout_spanish_latin"
+ android:label="@string/keyboard_layout_spanish_latin"
+ android:keyboardLayout="@raw/keyboard_layout_spanish_latin" />
</keyboard-layouts>
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 7c0735b..4db7d4d 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -121,13 +121,14 @@
filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
context.registerReceiver(mBroadcastReceiver, filter);
+ ConnectivityManager cm = (ConnectivityManager)
+ context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+
// get notified of phone state changes
TelephonyManager telephonyManager =
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
- ConnectivityManager cm = (ConnectivityManager)
- context.getSystemService(Context.CONNECTIVITY_SERVICE);
- mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
mAirplaneModeObserver);
diff --git a/rs/java/android/renderscript/RenderScriptGL.java b/rs/java/android/renderscript/RenderScriptGL.java
index c9cbe3e..d6841c8 100644
--- a/rs/java/android/renderscript/RenderScriptGL.java
+++ b/rs/java/android/renderscript/RenderScriptGL.java
@@ -232,9 +232,13 @@
validate();
//android.util.Log.v("rs", "set surface " + sur + " w=" + w + ", h=" + h);
+ Surface s = null;
+ if (sur != null) {
+ s = new Surface(sur);
+ }
mWidth = w;
mHeight = h;
- nContextSetSurfaceTexture(w, h, sur);
+ nContextSetSurface(w, h, s);
}
/**
diff --git a/services/common_time/common_time_server.cpp b/services/common_time/common_time_server.cpp
index 21e706f..3e11987 100644
--- a/services/common_time/common_time_server.cpp
+++ b/services/common_time/common_time_server.cpp
@@ -590,7 +590,7 @@
for (i = 0; (i < src_len) && (offset < dst_len); ++i) {
int res;
if (0 == (i % 16)) {
- res = snprintf(dst + offset, dst_len - offset, "\n%04x :", i);
+ res = snprintf(dst + offset, dst_len - offset, "\n%04zx :", i);
if (res < 0)
break;
offset += res;
diff --git a/services/common_time/utils.cpp b/services/common_time/utils.cpp
index ed2c77d..ddcdfe7 100644
--- a/services/common_time/utils.cpp
+++ b/services/common_time/utils.cpp
@@ -56,7 +56,7 @@
, mHeader(header) {
mRingBuffer = new Entry[mSize];
if (NULL == mRingBuffer)
- ALOGE("Failed to allocate log ring with %u entries.", mSize);
+ ALOGE("Failed to allocate log ring with %zu entries.", mSize);
}
LogRing::~LogRing() {
@@ -150,7 +150,7 @@
localtime_r(&mRingBuffer[ndx].first_ts.tv_sec, &t);
strftime(timebuf, sizeof(timebuf), kTimeFmt, &t);
- res = snprintf(buf, sizeof(buf), "[%2d] %s.%03ld :: %s%s\n",
+ res = snprintf(buf, sizeof(buf), "[%2zu] %s.%03ld :: %s%s\n",
i, timebuf,
mRingBuffer[ndx].first_ts.tv_usec / 1000,
mRingBuffer[ndx].s.string(),
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index fc64656..e3a3e17 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -759,7 +759,7 @@
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
// Device was removed before INotify noticed.
ALOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d "
- "capacity: %d errno: %d)\n",
+ "capacity: %zu errno: %d)\n",
device->fd, readSize, bufferSize, capacity, errno);
deviceChanged = true;
closeDeviceLocked(device);
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 10a639e..06a57d5 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -147,7 +147,7 @@
return false;
}
if (pointerCount < 1 || pointerCount > MAX_POINTERS) {
- ALOGE("Motion event has invalid pointer count %d; value must be between 1 and %d.",
+ ALOGE("Motion event has invalid pointer count %zu; value must be between 1 and %d.",
pointerCount, MAX_POINTERS);
return false;
}
@@ -3107,7 +3107,7 @@
dump.append(INDENT "TouchedWindows:\n");
for (size_t i = 0; i < mTouchState.windows.size(); i++) {
const TouchedWindow& touchedWindow = mTouchState.windows[i];
- dump.appendFormat(INDENT2 "%d: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
+ dump.appendFormat(INDENT2 "%zu: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n",
i, touchedWindow.windowHandle->getName().string(),
touchedWindow.pointerIds.value,
touchedWindow.targetFlags);
@@ -3122,7 +3122,7 @@
const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- dump.appendFormat(INDENT2 "%d: name='%s', displayId=%d, "
+ dump.appendFormat(INDENT2 "%zu: name='%s', displayId=%d, "
"paused=%s, hasFocus=%s, hasWallpaper=%s, "
"visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
"frame=[%d,%d][%d,%d], scale=%f, "
@@ -3152,7 +3152,7 @@
dump.append(INDENT "MonitoringChannels:\n");
for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
const sp<InputChannel>& channel = mMonitoringChannels[i];
- dump.appendFormat(INDENT2 "%d: '%s'\n", i, channel->getName().string());
+ dump.appendFormat(INDENT2 "%zu: '%s'\n", i, channel->getName().string());
}
} else {
dump.append(INDENT "MonitoringChannels: <none>\n");
@@ -3201,7 +3201,7 @@
dump.append(INDENT "Connections:\n");
for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
const sp<Connection>& connection = mConnectionsByFd.valueAt(i);
- dump.appendFormat(INDENT2 "%d: channelName='%s', windowName='%s', "
+ dump.appendFormat(INDENT2 "%zu: channelName='%s', windowName='%s', "
"status=%s, monitor=%s, inputPublisherBlocked=%s\n",
i, connection->getInputChannelName(), connection->getWindowName(),
connection->getStatusLabel(), toString(connection->monitor),
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 03852a5..d86fa4f 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -1992,7 +1992,7 @@
dumpParameters(dump);
dump.appendFormat(INDENT3 "KeyboardType: %d\n", mKeyboardType);
dump.appendFormat(INDENT3 "Orientation: %d\n", mOrientation);
- dump.appendFormat(INDENT3 "KeyDowns: %d keys currently down\n", mKeyDowns.size());
+ dump.appendFormat(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size());
dump.appendFormat(INDENT3 "MetaState: 0x%0x\n", mMetaState);
dump.appendFormat(INDENT3 "DownTime: %lld\n", mDownTime);
}
@@ -3372,7 +3372,7 @@
for (size_t i = 0; i < mVirtualKeys.size(); i++) {
const VirtualKey& virtualKey = mVirtualKeys.itemAt(i);
- dump.appendFormat(INDENT4 "%d: scanCode=%d, keyCode=%d, "
+ dump.appendFormat(INDENT4 "%zu: scanCode=%d, keyCode=%d, "
"hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n",
i, virtualKey.scanCode, virtualKey.keyCode,
virtualKey.hitLeft, virtualKey.hitRight,
@@ -6119,8 +6119,8 @@
&& mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
if (slotCount > MAX_SLOTS) {
- ALOGW("MultiTouch Device %s reported %d slots but the framework "
- "only supports a maximum of %d slots at this time.",
+ ALOGW("MultiTouch Device %s reported %zu slots but the framework "
+ "only supports a maximum of %zu slots at this time.",
getDeviceName().string(), slotCount, MAX_SLOTS);
slotCount = MAX_SLOTS;
}
@@ -6292,7 +6292,7 @@
// If there are too many axes, start dropping them.
// Prefer to keep explicitly mapped axes.
if (mAxes.size() > PointerCoords::MAX_AXES) {
- ALOGI("Joystick '%s' has %d axes but the framework only supports a maximum of %d.",
+ ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.",
getDeviceName().string(), mAxes.size(), PointerCoords::MAX_AXES);
pruneAxes(true);
pruneAxes(false);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d895137..ade8414 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -46,6 +46,7 @@
import com.android.internal.R;
import com.android.internal.os.BinderInternal;
+import com.android.internal.os.Zygote;
import com.android.internal.os.SamplingProfilerIntegration;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accounts.AccountManagerService;
@@ -71,7 +72,6 @@
import com.android.server.wm.WindowManagerService;
import dalvik.system.VMRuntime;
-import dalvik.system.Zygote;
import java.io.File;
import java.util.Timer;
@@ -824,7 +824,7 @@
if (safeMode) {
ActivityManagerService.self().enterSafeMode();
// Post the safe mode state in the Zygote class
- Zygote.systemInSafeMode = true;
+ SystemServer.inSafeMode = true;
// Disable the JIT for the system_server process
VMRuntime.getRuntime().disableJitCompilation();
} else {
@@ -1117,6 +1117,11 @@
// give any timezone code room without going into negative time.
private static final long EARLIEST_SUPPORTED_TIME = 86400 * 1000;
+ /**
+ * When set, all subsequent apps will be launched in safe mode.
+ */
+ public static boolean inSafeMode;
+
/**
* Called to initialize native system services.
*/
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 97696a5..16b9963 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -38,6 +38,7 @@
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TransferPipe;
+import com.android.internal.os.Zygote;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.MemInfoReader;
@@ -57,8 +58,6 @@
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
-import dalvik.system.Zygote;
-
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -2768,7 +2767,7 @@
// Run the app in safe mode if its manifest requests so or the
// system is booted in safe mode.
if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 ||
- Zygote.systemInSafeMode == true) {
+ SystemServer.inSafeMode == true) {
debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
}
if ("1".equals(SystemProperties.get("debug.checkjni"))) {
@@ -2781,11 +2780,16 @@
debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
}
+ String requiredAbi = app.info.requiredCpuAbi;
+ if (requiredAbi == null) {
+ requiredAbi = Build.SUPPORTED_ABIS[0];
+ }
+
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
app.processName, uid, uid, gids, debugFlags, mountExternal,
- app.info.targetSdkVersion, app.info.seinfo, null);
+ app.info.targetSdkVersion, app.info.seinfo, requiredAbi, null);
BatteryStatsImpl bs = mBatteryStatsService.getActiveStatistics();
synchronized (bs) {
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
old mode 100644
new mode 100755
index 49f29fe..63a52e6
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -942,8 +942,8 @@
// for another app to start, then we have paused dispatching
// for this activity.
ActivityRecord r = this;
- final ActivityStack stack = task.stack;
if (r.waitingVisible) {
+ final ActivityStack stack = mStackSupervisor.getFocusedStack();
// Hmmm, who might we be waiting for?
r = stack.mResumedActivity;
if (r == null) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index d651a62..07c2201 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1142,6 +1142,7 @@
// At this point, nothing else needs to be shown
if (DEBUG_VISBILITY) Slog.v(TAG, "Fullscreen: at " + r);
behindFullscreen = true;
+ showHomeBehindStack = false;
} else if (isActivityOverHome(r)) {
if (DEBUG_VISBILITY) Slog.v(TAG, "Showing home: at " + r);
showHomeBehindStack = true;
@@ -2624,6 +2625,9 @@
if (mResumedActivity == r) {
mResumedActivity = null;
}
+ if (mPausingActivity == r) {
+ mPausingActivity = null;
+ }
if (mService.mFocusedActivity == r) {
mService.mFocusedActivity = null;
}
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
old mode 100644
new mode 100755
index 483b4a0..93de0a6
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1425,6 +1425,7 @@
r.resultTo = null;
}
+ boolean switchStackFromBg = false;
boolean addingToTask = false;
boolean movedHome = false;
TaskRecord reuseTask = null;
@@ -1486,6 +1487,11 @@
}
options = null;
}
+ } else {
+ switchStackFromBg = lastStack != targetStack;
+ if (DEBUG_TASKS) Slog.d(TAG, "Caller " + sourceRecord
+ + " is not top task, it may not move " + r
+ + " to front, switchStack=" + switchStackFromBg);
}
// If the caller has requested that the target task be
// reset, then do so.
@@ -1593,6 +1599,10 @@
// don't use that intent!) And for paranoia, make
// sure we have correctly resumed the top activity.
if (doResume) {
+ if (switchStackFromBg) {
+ moveHomeStack(lastStack.isHomeStack());
+ targetStack = lastStack;
+ }
targetStack.resumeTopActivityLocked(null, options);
} else {
ActivityOptions.abort(options);
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index 4b087cc..8cd9d93 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -397,7 +397,14 @@
return execute(builder.toString());
}
- public boolean restoreconData() {
- return (execute("restorecondata") == 0);
+ public boolean restoreconData(String pkgName, String seinfo, int uid) {
+ StringBuilder builder = new StringBuilder("restorecondata");
+ builder.append(' ');
+ builder.append(pkgName);
+ builder.append(' ');
+ builder.append(seinfo != null ? seinfo : "!");
+ builder.append(' ');
+ builder.append(uid);
+ return (execute(builder.toString()) == 0);
}
}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 5dded57..44765a5 100755
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -59,8 +59,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
-import android.content.ServiceConnection;
import android.content.IntentSender.SendIntentException;
+import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ContainerEncryptionParams;
@@ -72,14 +72,15 @@
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
+import android.content.pm.ManifestDigest;
import android.content.pm.PackageCleanItem;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
-import android.content.pm.PackageUserState;
import android.content.pm.PackageParser.ActivityIntentInfo;
import android.content.pm.PackageStats;
+import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -87,7 +88,6 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
-import android.content.pm.ManifestDigest;
import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
@@ -97,6 +97,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
+import android.os.Environment.UserEnvironment;
import android.os.FileObserver;
import android.os.FileUtils;
import android.os.Handler;
@@ -113,7 +114,6 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
-import android.os.Environment.UserEnvironment;
import android.os.UserManager;
import android.security.KeyStore;
import android.security.SystemKeyStore;
@@ -393,6 +393,9 @@
// If mac_permissions.xml was found for seinfo labeling.
boolean mFoundPolicyFile;
+ // If a recursive restorecon of /data/data/<pkg> is needed.
+ private boolean mShouldRestoreconData = SELinuxMMAC.shouldRestorecon();
+
// All available activities, for your resolving pleasure.
final ActivityIntentResolver mActivities =
new ActivityIntentResolver();
@@ -1486,13 +1489,6 @@
// can downgrade to reader
mSettings.writeLPr();
- if (SELinuxMMAC.shouldRestorecon()) {
- Slog.i(TAG, "Relabeling of /data/data and /data/user issued.");
- if (mInstaller.restoreconData()) {
- SELinuxMMAC.setRestoreconDone();
- }
- }
-
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
@@ -2009,6 +2005,7 @@
pkg.applicationInfo.dataDir =
getDataPathForPackage(packageName, 0).getPath();
pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
+ pkg.applicationInfo.requiredCpuAbi = ps.requiredCpuAbiString;
}
return generatePackageInfo(pkg, flags, userId);
}
@@ -3827,6 +3824,8 @@
codePath = pkg.mScanPath;
// Set application objects path explicitly.
setApplicationInfoPaths(pkg, codePath, resPath);
+ // Applications can run with the primary Cpu Abi unless otherwise is specified
+ pkg.applicationInfo.requiredCpuAbi = null;
// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
| SCAN_UPDATE_SIGNATURE, currentTime, user);
@@ -4399,6 +4398,7 @@
// the PkgSetting exists already and doesn't have to be created.
pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
destResourceFile, pkg.applicationInfo.nativeLibraryDir,
+ pkg.applicationInfo.requiredCpuAbi,
pkg.applicationInfo.flags, user, false);
if (pkgSetting == null) {
Slog.w(TAG, "Creating application package " + pkg.packageName + " failed");
@@ -4617,6 +4617,11 @@
}
}
pkg.applicationInfo.dataDir = dataPath.getPath();
+ if (mShouldRestoreconData) {
+ Slog.i(TAG, "SELinux relabeling of " + pkg.packageName + " issued.");
+ mInstaller.restoreconData(pkg.packageName, pkg.applicationInfo.seinfo,
+ pkg.applicationInfo.uid);
+ }
} else {
if (DEBUG_PACKAGE_SCANNING) {
if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
@@ -4699,11 +4704,20 @@
}
try {
- if (copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir) != PackageManager.INSTALL_SUCCEEDED) {
+ int copyRet = copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir);
+ if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
Slog.e(TAG, "Unable to copy native libraries");
mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
return null;
}
+
+ // We've successfully copied native libraries across, so we make a
+ // note of what ABI we're using
+ if (copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
+ pkg.applicationInfo.requiredCpuAbi = Build.SUPPORTED_ABIS[copyRet];
+ } else {
+ pkg.applicationInfo.requiredCpuAbi = null;
+ }
} catch (IOException e) {
Slog.e(TAG, "Unable to copy native libraries", e);
mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
@@ -5250,7 +5264,21 @@
* If this is an internal application or our nativeLibraryPath points to
* the app-lib directory, unpack the libraries if necessary.
*/
- return NativeLibraryHelper.copyNativeBinariesIfNeededLI(scanFile, nativeLibraryDir);
+ final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
+ try {
+ int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS);
+ if (abi >= 0) {
+ int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
+ nativeLibraryDir, Build.SUPPORTED_ABIS[abi]);
+ if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
+ return copyRet;
+ }
+ }
+
+ return abi;
+ } finally {
+ handle.close();
+ }
}
private void killApplication(String pkgName, int appId, String reason) {
@@ -8136,7 +8164,7 @@
}
try {
int copyRet = copyNativeLibrariesForInternalApp(codeFile, nativeLibraryFile);
- if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
+ if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
return copyRet;
}
} catch (IOException e) {
@@ -11062,6 +11090,10 @@
*/
public void scanAvailableAsecs() {
updateExternalMediaStatusInner(true, false, false);
+ if (mShouldRestoreconData) {
+ SELinuxMMAC.setRestoreconDone();
+ mShouldRestoreconData = false;
+ }
}
/*
@@ -11507,8 +11539,17 @@
final File newNativeDir = new File(newNativePath);
if (!isForwardLocked(pkg) && !isExternal(pkg)) {
- NativeLibraryHelper.copyNativeBinariesIfNeededLI(
- new File(newCodePath), newNativeDir);
+ // NOTE: We do not report any errors from the APK scan and library
+ // copy at this point.
+ NativeLibraryHelper.ApkHandle handle =
+ new NativeLibraryHelper.ApkHandle(newCodePath);
+ final int abi = NativeLibraryHelper.findSupportedAbi(
+ handle, Build.SUPPORTED_ABIS);
+ if (abi >= 0) {
+ NativeLibraryHelper.copyNativeBinariesIfNeededLI(
+ handle, newNativeDir, Build.SUPPORTED_ABIS[abi]);
+ }
+ handle.close();
}
final int[] users = sUserManager.getUserIds();
for (int user : users) {
diff --git a/services/java/com/android/server/pm/PackageSetting.java b/services/java/com/android/server/pm/PackageSetting.java
index b447861..15df3d2 100644
--- a/services/java/com/android/server/pm/PackageSetting.java
+++ b/services/java/com/android/server/pm/PackageSetting.java
@@ -30,8 +30,8 @@
SharedUserSetting sharedUser;
PackageSetting(String name, String realName, File codePath, File resourcePath,
- String nativeLibraryPathString, int pVersionCode, int pkgFlags) {
- super(name, realName, codePath, resourcePath, nativeLibraryPathString, pVersionCode,
+ String nativeLibraryPathString, String requiredCpuAbiString, int pVersionCode, int pkgFlags) {
+ super(name, realName, codePath, resourcePath, nativeLibraryPathString, requiredCpuAbiString, pVersionCode,
pkgFlags);
}
diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java
index 7747c8f..ba95b9a 100644
--- a/services/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/java/com/android/server/pm/PackageSettingBase.java
@@ -53,6 +53,7 @@
File resourcePath;
String resourcePathString;
String nativeLibraryPathString;
+ String requiredCpuAbiString;
long timeStamp;
long firstInstallTime;
long lastUpdateTime;
@@ -80,11 +81,11 @@
/* package name of the app that installed this package */
String installerPackageName;
PackageSettingBase(String name, String realName, File codePath, File resourcePath,
- String nativeLibraryPathString, int pVersionCode, int pkgFlags) {
+ String nativeLibraryPathString, String requiredCpuAbiString, int pVersionCode, int pkgFlags) {
super(pkgFlags);
this.name = name;
this.realName = realName;
- init(codePath, resourcePath, nativeLibraryPathString, pVersionCode);
+ init(codePath, resourcePath, nativeLibraryPathString, requiredCpuAbiString, pVersionCode);
}
/**
@@ -101,6 +102,7 @@
resourcePath = base.resourcePath;
resourcePathString = base.resourcePathString;
nativeLibraryPathString = base.nativeLibraryPathString;
+ requiredCpuAbiString = base.requiredCpuAbiString;
timeStamp = base.timeStamp;
firstInstallTime = base.firstInstallTime;
lastUpdateTime = base.lastUpdateTime;
@@ -128,12 +130,13 @@
}
void init(File codePath, File resourcePath, String nativeLibraryPathString,
- int pVersionCode) {
+ String requiredCpuAbiString, int pVersionCode) {
this.codePath = codePath;
this.codePathString = codePath.toString();
this.resourcePath = resourcePath;
this.resourcePathString = resourcePath.toString();
this.nativeLibraryPathString = nativeLibraryPathString;
+ this.requiredCpuAbiString = requiredCpuAbiString;
this.versionCode = pVersionCode;
}
@@ -164,6 +167,7 @@
grantedPermissions = base.grantedPermissions;
gids = base.gids;
+ requiredCpuAbiString = base.requiredCpuAbiString;
timeStamp = base.timeStamp;
firstInstallTime = base.firstInstallTime;
lastUpdateTime = base.lastUpdateTime;
diff --git a/services/java/com/android/server/pm/PendingPackage.java b/services/java/com/android/server/pm/PendingPackage.java
index c17cc46..36c3a34 100644
--- a/services/java/com/android/server/pm/PendingPackage.java
+++ b/services/java/com/android/server/pm/PendingPackage.java
@@ -22,8 +22,8 @@
final int sharedId;
PendingPackage(String name, String realName, File codePath, File resourcePath,
- String nativeLibraryPathString, int sharedId, int pVersionCode, int pkgFlags) {
- super(name, realName, codePath, resourcePath, nativeLibraryPathString, pVersionCode,
+ String nativeLibraryPathString, String requiredCpuAbiString, int sharedId, int pVersionCode, int pkgFlags) {
+ super(name, realName, codePath, resourcePath, nativeLibraryPathString, requiredCpuAbiString, pVersionCode,
pkgFlags);
this.sharedId = sharedId;
}
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index e599409..a50c689 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -213,10 +213,10 @@
PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage,
String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
- String nativeLibraryPathString, int pkgFlags, UserHandle user, boolean add) {
+ String nativeLibraryPathString, String requiredCpuAbiString, int pkgFlags, UserHandle user, boolean add) {
final String name = pkg.packageName;
PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath,
- resourcePath, nativeLibraryPathString, pkg.mVersionCode, pkgFlags,
+ resourcePath, nativeLibraryPathString, requiredCpuAbiString, pkg.mVersionCode, pkgFlags,
user, add, true /* allowInstall */);
return p;
}
@@ -298,7 +298,7 @@
p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,
- p.nativeLibraryPathString, p.appId, p.versionCode, p.pkgFlags);
+ p.nativeLibraryPathString, p.requiredCpuAbiString, p.appId, p.versionCode, p.pkgFlags);
mDisabledSysPackages.remove(name);
return ret;
}
@@ -312,7 +312,7 @@
}
PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath,
- String nativeLibraryPathString, int uid, int vc, int pkgFlags) {
+ String nativeLibraryPathString, String requiredCpuAbiString, int uid, int vc, int pkgFlags) {
PackageSetting p = mPackages.get(name);
if (p != null) {
if (p.appId == uid) {
@@ -322,7 +322,7 @@
"Adding duplicate package, keeping first: " + name);
return null;
}
- p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString,
+ p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString, requiredCpuAbiString,
vc, pkgFlags);
p.appId = uid;
if (addUserIdLPw(uid, p, name)) {
@@ -391,10 +391,11 @@
private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
- String nativeLibraryPathString, int vc, int pkgFlags,
+ String nativeLibraryPathString, String requiredCpuAbiString, int vc, int pkgFlags,
UserHandle installUser, boolean add, boolean allowInstall) {
PackageSetting p = mPackages.get(name);
if (p != null) {
+ p.requiredCpuAbiString = requiredCpuAbiString;
if (!p.codePath.equals(codePath)) {
// Check to see if its a disabled system app
if ((p.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
@@ -438,7 +439,7 @@
if (origPackage != null) {
// We are consuming the data from an existing package.
p = new PackageSetting(origPackage.name, name, codePath, resourcePath,
- nativeLibraryPathString, vc, pkgFlags);
+ nativeLibraryPathString, requiredCpuAbiString, vc, pkgFlags);
if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
+ name + " is adopting original package " + origPackage.name);
// Note that we will retain the new package's signature so
@@ -455,7 +456,7 @@
p.setTimeStamp(codePath.lastModified());
} else {
p = new PackageSetting(name, realName, codePath, resourcePath,
- nativeLibraryPathString, vc, pkgFlags);
+ nativeLibraryPathString, requiredCpuAbiString, vc, pkgFlags);
p.setTimeStamp(codePath.lastModified());
p.sharedUser = sharedUser;
// If this is not a system app, it starts out stopped.
@@ -581,6 +582,8 @@
&& !nativeLibraryPath.equalsIgnoreCase(p.nativeLibraryPathString)) {
p.nativeLibraryPathString = nativeLibraryPath;
}
+ // Update the required Cpu Abi
+ p.requiredCpuAbiString = pkg.applicationInfo.requiredCpuAbi;
// Update version code if needed
if (pkg.mVersionCode != p.versionCode) {
p.versionCode = pkg.mVersionCode;
@@ -1427,6 +1430,7 @@
// FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES:
// system/core/run-as/run-as.c
// system/core/sdcard/sdcard.c
+ // external/libselinux/src/android.c:package_info_init()
//
sb.setLength(0);
sb.append(ai.packageName);
@@ -1497,6 +1501,9 @@
if (pkg.nativeLibraryPathString != null) {
serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString);
}
+ if (pkg.requiredCpuAbiString != null) {
+ serializer.attribute(null, "requiredCpuAbi", pkg.requiredCpuAbiString);
+ }
if (pkg.sharedUser == null) {
serializer.attribute(null, "userId", Integer.toString(pkg.appId));
} else {
@@ -1539,6 +1546,9 @@
if (pkg.nativeLibraryPathString != null) {
serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString);
}
+ if (pkg.requiredCpuAbiString != null) {
+ serializer.attribute(null, "requiredCpuAbi", pkg.requiredCpuAbiString);
+ }
serializer.attribute(null, "flags", Integer.toString(pkg.pkgFlags));
serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp));
serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime));
@@ -1803,7 +1813,7 @@
if (idObj != null && idObj instanceof SharedUserSetting) {
PackageSetting p = getPackageLPw(pp.name, null, pp.realName,
(SharedUserSetting) idObj, pp.codePath, pp.resourcePath,
- pp.nativeLibraryPathString, pp.versionCode, pp.pkgFlags,
+ pp.nativeLibraryPathString, pp.requiredCpuAbiString, pp.versionCode, pp.pkgFlags,
null, true /* add */, false /* allowInstall */);
if (p == null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -2223,6 +2233,8 @@
String codePathStr = parser.getAttributeValue(null, "codePath");
String resourcePathStr = parser.getAttributeValue(null, "resourcePath");
String nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
+ String requiredCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi");
+
if (resourcePathStr == null) {
resourcePathStr = codePathStr;
}
@@ -2242,7 +2254,7 @@
pkgFlags |= ApplicationInfo.FLAG_PRIVILEGED;
}
PackageSetting ps = new PackageSetting(name, realName, codePathFile,
- new File(resourcePathStr), nativeLibraryPathStr, versionCode, pkgFlags);
+ new File(resourcePathStr), nativeLibraryPathStr, requiredCpuAbiString, versionCode, pkgFlags);
String timeStampStr = parser.getAttributeValue(null, "ft");
if (timeStampStr != null) {
try {
@@ -2309,6 +2321,7 @@
String codePathStr = null;
String resourcePathStr = null;
String nativeLibraryPathStr = null;
+ String requiredCpuAbiString = null;
String systemStr = null;
String installerPackageName = null;
String uidError = null;
@@ -2328,6 +2341,8 @@
codePathStr = parser.getAttributeValue(null, "codePath");
resourcePathStr = parser.getAttributeValue(null, "resourcePath");
nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
+ requiredCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi");
+
version = parser.getAttributeValue(null, "version");
if (version != null) {
try {
@@ -2404,7 +2419,7 @@
+ parser.getPositionDescription());
} else if (userId > 0) {
packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
- new File(resourcePathStr), nativeLibraryPathStr, userId, versionCode,
+ new File(resourcePathStr), nativeLibraryPathStr, requiredCpuAbiString, userId, versionCode,
pkgFlags);
if (PackageManagerService.DEBUG_SETTINGS)
Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
@@ -2422,7 +2437,7 @@
userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
if (userId > 0) {
packageSetting = new PendingPackage(name.intern(), realName, new File(
- codePathStr), new File(resourcePathStr), nativeLibraryPathStr, userId,
+ codePathStr), new File(resourcePathStr), nativeLibraryPathStr, requiredCpuAbiString, userId,
versionCode, pkgFlags);
packageSetting.setTimeStamp(timeStamp);
packageSetting.firstInstallTime = firstInstallTime;
@@ -2451,6 +2466,7 @@
packageSetting.uidError = "true".equals(uidError);
packageSetting.installerPackageName = installerPackageName;
packageSetting.nativeLibraryPathString = nativeLibraryPathStr;
+ packageSetting.requiredCpuAbiString = requiredCpuAbiString;
// Handle legacy string here for single-user mode
final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
if (enabledStr != null) {
@@ -2914,6 +2930,7 @@
pw.print(prefix); pw.print(" codePath="); pw.println(ps.codePathString);
pw.print(prefix); pw.print(" resourcePath="); pw.println(ps.resourcePathString);
pw.print(prefix); pw.print(" nativeLibraryPath="); pw.println(ps.nativeLibraryPathString);
+ pw.print(prefix); pw.print(" requiredCpuAbi="); pw.println(ps.requiredCpuAbiString);
pw.print(prefix); pw.print(" versionCode="); pw.print(ps.versionCode);
if (ps.pkg != null) {
pw.print(" targetSdk="); pw.print(ps.pkg.applicationInfo.targetSdkVersion);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 2ab7e36..b6c6770 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -2373,6 +2373,11 @@
}
public void removeWindowLocked(Session session, WindowState win) {
+ removeWindowLocked(session, win, false);
+ }
+
+ private void removeWindowLocked(Session session, WindowState win,
+ boolean forceRemove) {
if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Starting window removed " + win);
removeStartingWindowTimeout(win.mAppToken);
@@ -2423,7 +2428,7 @@
mDisplayMagnifier.onWindowTransitionLocked(win, transit);
}
}
- if (win.mExiting || win.mWinAnimator.isAnimating()) {
+ if (!forceRemove && (win.mExiting || win.mWinAnimator.isAnimating())) {
// The exit animation is running... wait for it!
//Slog.i(TAG, "*** Running exit animation...");
win.mExiting = true;
@@ -10852,7 +10857,7 @@
WindowList windows = displayContent.getWindowList();
while (!windows.isEmpty()) {
final WindowState win = windows.get(windows.size() - 1);
- removeWindowLocked(win.mSession, win);
+ removeWindowLocked(win.mSession, win, true);
}
}
mAnimator.removeDisplayLocked(displayId);
diff --git a/services/jni/com_android_server_AlarmManagerService.cpp b/services/jni/com_android_server_AlarmManagerService.cpp
index c26a516..a58b00bce 100644
--- a/services/jni/com_android_server_AlarmManagerService.cpp
+++ b/services/jni/com_android_server_AlarmManagerService.cpp
@@ -365,7 +365,9 @@
int result = impl->set(type, &ts);
if (result < 0)
{
- ALOGE("Unable to set alarm to %lld.%09lld: %s\n", seconds, nanoseconds, strerror(errno));
+ ALOGE("Unable to set alarm to %lld.%09lld: %s\n",
+ static_cast<long long>(seconds),
+ static_cast<long long>(nanoseconds), strerror(errno));
}
}
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index 323e0ac..1c75658 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -101,8 +101,93 @@
/** Unknown error or not specified */
public static final int ERROR_UNSPECIFIED = 36;
+ /** Smallest valid value for call disconnect codes. */
+ public static final int MINIMUM_VALID_VALUE = NOT_DISCONNECTED;
+ /** Largest valid value for call disconnect codes. */
+ public static final int MAXIMUM_VALID_VALUE = ERROR_UNSPECIFIED;
+
/** Private constructor to avoid class instantiation. */
private DisconnectCause() {
// Do nothing.
}
+
+ /** Returns descriptive string for the specified disconnect cause. */
+ public static String toString(int cause) {
+ switch (cause) {
+ case NOT_DISCONNECTED:
+ return "NOT_DISCONNECTED";
+ case INCOMING_MISSED:
+ return "INCOMING_MISSED";
+ case NORMAL:
+ return "NORMAL";
+ case LOCAL:
+ return "LOCAL";
+ case BUSY:
+ return "BUSY";
+ case CONGESTION:
+ return "CONGESTION";
+ case INVALID_NUMBER:
+ return "INVALID_NUMBER";
+ case NUMBER_UNREACHABLE:
+ return "NUMBER_UNREACHABLE";
+ case SERVER_UNREACHABLE:
+ return "SERVER_UNREACHABLE";
+ case INVALID_CREDENTIALS:
+ return "INVALID_CREDENTIALS";
+ case OUT_OF_NETWORK:
+ return "OUT_OF_NETWORK";
+ case SERVER_ERROR:
+ return "SERVER_ERROR";
+ case TIMED_OUT:
+ return "TIMED_OUT";
+ case LOST_SIGNAL:
+ return "LOST_SIGNAL";
+ case LIMIT_EXCEEDED:
+ return "LIMIT_EXCEEDED";
+ case INCOMING_REJECTED:
+ return "INCOMING_REJECTED";
+ case POWER_OFF:
+ return "POWER_OFF";
+ case OUT_OF_SERVICE:
+ return "OUT_OF_SERVICE";
+ case ICC_ERROR:
+ return "ICC_ERROR";
+ case CALL_BARRED:
+ return "CALL_BARRED";
+ case FDN_BLOCKED:
+ return "FDN_BLOCKED";
+ case CS_RESTRICTED:
+ return "CS_RESTRICTED";
+ case CS_RESTRICTED_NORMAL:
+ return "CS_RESTRICTED_NORMAL";
+ case CS_RESTRICTED_EMERGENCY:
+ return "CS_RESTRICTED_EMERGENCY";
+ case UNOBTAINABLE_NUMBER:
+ return "UNOBTAINABLE_NUMBER";
+ case CDMA_LOCKED_UNTIL_POWER_CYCLE:
+ return "CDMA_LOCKED_UNTIL_POWER_CYCLE";
+ case CDMA_DROP:
+ return "CDMA_DROP";
+ case CDMA_INTERCEPT:
+ return "CDMA_INTERCEPT";
+ case CDMA_REORDER:
+ return "CDMA_REORDER";
+ case CDMA_SO_REJECT:
+ return "CDMA_SO_REJECT";
+ case CDMA_RETRY_ORDER:
+ return "CDMA_RETRY_ORDER";
+ case CDMA_ACCESS_FAILURE:
+ return "CDMA_ACCESS_FAILURE";
+ case CDMA_PREEMPTED:
+ return "CDMA_PREEMPTED";
+ case CDMA_NOT_EMERGENCY:
+ return "CDMA_NOT_EMERGENCY";
+ case CDMA_ACCESS_BLOCKED:
+ return "CDMA_ACCESS_BLOCKED";
+ case ERROR_UNSPECIFIED:
+ return "ERROR_UNSPECIFIED";
+ default:
+ return "INVALID";
+ }
+ }
}
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 38bf540..0b1f985 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -1317,9 +1317,9 @@
curIsFormatted = false;
// Untranslatable strings must only exist in the default [empty] locale
if (locale.size() > 0) {
- fprintf(stderr, "aapt: warning: string '%s' in %s marked untranslatable but exists"
- " in locale '%s'\n", String8(name).string(),
- bundle->getResourceSourceDirs()[0],
+ SourcePos(in->getPrintableSource(), block.getLineNumber()).warning(
+ "string '%s' marked untranslatable but exists in locale '%s'\n",
+ String8(name).string(),
locale.string());
// hasErrors = localHasErrors = true;
} else {
@@ -1330,7 +1330,10 @@
// having no default translation.
}
} else {
- outTable->addLocalization(name, locale);
+ outTable->addLocalization(
+ name,
+ locale,
+ SourcePos(in->getPrintableSource(), block.getLineNumber()));
}
if (formatted == false16) {
@@ -2570,9 +2573,9 @@
void
-ResourceTable::addLocalization(const String16& name, const String8& locale)
+ResourceTable::addLocalization(const String16& name, const String8& locale, const SourcePos& src)
{
- mLocalizations[name].insert(locale);
+ mLocalizations[name][locale] = src;
}
@@ -2592,21 +2595,22 @@
const String8 defaultLocale;
// For all strings...
- for (map<String16, set<String8> >::iterator nameIter = mLocalizations.begin();
+ for (map<String16, map<String8, SourcePos> >::iterator nameIter = mLocalizations.begin();
nameIter != mLocalizations.end();
nameIter++) {
- const set<String8>& configSet = nameIter->second; // naming convenience
+ const map<String8, SourcePos>& configSrcMap = nameIter->second;
// Look for strings with no default localization
- if (configSet.count(defaultLocale) == 0) {
- fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
- String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
- for (set<String8>::const_iterator locales = configSet.begin();
- locales != configSet.end();
- locales++) {
- fprintf(stdout, " %s", (*locales).string());
+ if (configSrcMap.count(defaultLocale) == 0) {
+ SourcePos().warning("string '%s' has no default translation.",
+ String8(nameIter->first).string());
+ if (mBundle->getVerbose()) {
+ for (map<String8, SourcePos>::const_iterator locales = configSrcMap.begin();
+ locales != configSrcMap.end();
+ locales++) {
+ locales->second.printf("locale %s found", locales->first.string());
+ }
}
- fprintf(stdout, "\n");
// !!! TODO: throw an error here in some circumstances
}
@@ -2616,6 +2620,8 @@
const char* start = allConfigs;
const char* comma;
+ set<String8> missingConfigs;
+ AaptLocaleValue locale;
do {
String8 config;
comma = strchr(start, ',');
@@ -2626,27 +2632,38 @@
config.setTo(start);
}
+ if (!locale.initFromFilterString(config)) {
+ continue;
+ }
+
// don't bother with the pseudolocale "zz_ZZ"
if (config != "zz_ZZ") {
- if (configSet.find(config) == configSet.end()) {
+ if (configSrcMap.find(config) == configSrcMap.end()) {
// okay, no specific localization found. it's possible that we are
// requiring a specific regional localization [e.g. de_DE] but there is an
// available string in the generic language localization [e.g. de];
// consider that string to have fulfilled the localization requirement.
String8 region(config.string(), 2);
- if (configSet.find(region) == configSet.end()) {
- if (configSet.count(defaultLocale) == 0) {
- fprintf(stdout, "aapt: warning: "
- "**** string '%s' has no default or required localization "
- "for '%s' in %s\n",
- String8(nameIter->first).string(),
- config.string(),
- mBundle->getResourceSourceDirs()[0]);
- }
+ if (configSrcMap.find(region) == configSrcMap.end() &&
+ configSrcMap.count(defaultLocale) == 0) {
+ missingConfigs.insert(config);
}
}
}
- } while (comma != NULL);
+ } while (comma != NULL);
+
+ if (!missingConfigs.empty()) {
+ String8 configStr;
+ for (set<String8>::iterator iter = missingConfigs.begin();
+ iter != missingConfigs.end();
+ iter++) {
+ configStr.appendFormat(" %s", iter->string());
+ }
+ SourcePos().warning("string '%s' is missing %u required localizations:%s",
+ String8(nameIter->first).string(),
+ (unsigned int)missingConfigs.size(),
+ configStr.string());
+ }
}
}
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index a3e0666..75005cd 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -220,7 +220,7 @@
status_t assignResourceIds();
status_t addSymbols(const sp<AaptSymbols>& outSymbols = NULL);
- void addLocalization(const String16& name, const String8& locale);
+ void addLocalization(const String16& name, const String8& locale, const SourcePos& src);
status_t validateLocalizations(void);
status_t flatten(Bundle*, const sp<AaptFile>& dest);
@@ -551,7 +551,7 @@
Bundle* mBundle;
// key = string resource name, value = set of locales in which that name is defined
- map<String16, set<String8> > mLocalizations;
+ map<String16, map<String8, SourcePos> > mLocalizations;
};
#endif
diff --git a/tools/aapt/SourcePos.cpp b/tools/aapt/SourcePos.cpp
index e2a921c..ae25047 100644
--- a/tools/aapt/SourcePos.cpp
+++ b/tools/aapt/SourcePos.cpp
@@ -10,17 +10,20 @@
// =============================================================================
struct ErrorPos
{
+ enum Level {
+ NOTE,
+ WARNING,
+ ERROR
+ };
+
String8 file;
int line;
String8 error;
- bool fatal;
+ Level level;
ErrorPos();
ErrorPos(const ErrorPos& that);
- ErrorPos(const String8& file, int line, const String8& error, bool fatal);
- ~ErrorPos();
- bool operator<(const ErrorPos& rhs) const;
- bool operator==(const ErrorPos& rhs) const;
+ ErrorPos(const String8& file, int line, const String8& error, Level level);
ErrorPos& operator=(const ErrorPos& rhs);
void print(FILE* to) const;
@@ -29,7 +32,7 @@
static vector<ErrorPos> g_errors;
ErrorPos::ErrorPos()
- :line(-1), fatal(false)
+ :line(-1), level(NOTE)
{
}
@@ -37,61 +40,52 @@
:file(that.file),
line(that.line),
error(that.error),
- fatal(that.fatal)
+ level(that.level)
{
}
-ErrorPos::ErrorPos(const String8& f, int l, const String8& e, bool fat)
+ErrorPos::ErrorPos(const String8& f, int l, const String8& e, Level lev)
:file(f),
line(l),
error(e),
- fatal(fat)
+ level(lev)
{
}
-ErrorPos::~ErrorPos()
-{
-}
-
-bool
-ErrorPos::operator<(const ErrorPos& rhs) const
-{
- if (this->file < rhs.file) return true;
- if (this->file == rhs.file) {
- if (this->line < rhs.line) return true;
- if (this->line == rhs.line) {
- if (this->error < rhs.error) return true;
- }
- }
- return false;
-}
-
-bool
-ErrorPos::operator==(const ErrorPos& rhs) const
-{
- return this->file == rhs.file
- && this->line == rhs.line
- && this->error == rhs.error;
-}
-
ErrorPos&
ErrorPos::operator=(const ErrorPos& rhs)
{
this->file = rhs.file;
this->line = rhs.line;
this->error = rhs.error;
+ this->level = rhs.level;
return *this;
}
void
ErrorPos::print(FILE* to) const
{
- const char* type = fatal ? "error:" : "warning:";
+ const char* type = "";
+ switch (level) {
+ case NOTE:
+ type = "note: ";
+ break;
+ case WARNING:
+ type = "warning: ";
+ break;
+ case ERROR:
+ type = "error: ";
+ break;
+ }
- if (this->line >= 0) {
- fprintf(to, "%s:%d: %s %s\n", this->file.string(), this->line, type, this->error.string());
+ if (!this->file.isEmpty()) {
+ if (this->line >= 0) {
+ fprintf(to, "%s:%d: %s%s\n", this->file.string(), this->line, type, this->error.string());
+ } else {
+ fprintf(to, "%s: %s%s\n", this->file.string(), type, this->error.string());
+ }
} else {
- fprintf(to, "%s: %s %s\n", this->file.string(), type, this->error.string());
+ fprintf(to, "%s%s\n", type, this->error.string());
}
}
@@ -116,40 +110,34 @@
{
}
-int
+void
SourcePos::error(const char* fmt, ...) const
{
- int retval=0;
- char buf[1024];
va_list ap;
va_start(ap, fmt);
- retval = vsnprintf(buf, sizeof(buf), fmt, ap);
+ String8 msg = String8::formatV(fmt, ap);
va_end(ap);
- char* p = buf + retval - 1;
- while (p > buf && *p == '\n') {
- *p = '\0';
- p--;
- }
- g_errors.push_back(ErrorPos(this->file, this->line, String8(buf), true));
- return retval;
+ g_errors.push_back(ErrorPos(this->file, this->line, msg, ErrorPos::ERROR));
}
-int
+void
SourcePos::warning(const char* fmt, ...) const
{
- int retval=0;
- char buf[1024];
va_list ap;
va_start(ap, fmt);
- retval = vsnprintf(buf, sizeof(buf), fmt, ap);
+ String8 msg = String8::formatV(fmt, ap);
va_end(ap);
- char* p = buf + retval - 1;
- while (p > buf && *p == '\n') {
- *p = '\0';
- p--;
- }
- ErrorPos(this->file, this->line, String8(buf), false).print(stderr);
- return retval;
+ ErrorPos(this->file, this->line, msg, ErrorPos::WARNING).print(stderr);
+}
+
+void
+SourcePos::printf(const char* fmt, ...) const
+{
+ va_list ap;
+ va_start(ap, fmt);
+ String8 msg = String8::formatV(fmt, ap);
+ va_end(ap);
+ ErrorPos(this->file, this->line, msg, ErrorPos::NOTE).print(stderr);
}
bool
diff --git a/tools/aapt/SourcePos.h b/tools/aapt/SourcePos.h
index 33f72a9..4ce817f 100644
--- a/tools/aapt/SourcePos.h
+++ b/tools/aapt/SourcePos.h
@@ -17,8 +17,9 @@
SourcePos();
~SourcePos();
- int error(const char* fmt, ...) const;
- int warning(const char* fmt, ...) const;
+ void error(const char* fmt, ...) const;
+ void warning(const char* fmt, ...) const;
+ void printf(const char* fmt, ...) const;
static bool hasErrors();
static void printErrors(FILE* to);