diff options
Diffstat (limited to 'core/java')
86 files changed, 1801 insertions, 606 deletions
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java index 43014ad..21f6eda 100644 --- a/core/java/android/animation/PropertyValuesHolder.java +++ b/core/java/android/animation/PropertyValuesHolder.java @@ -743,9 +743,9 @@ public class PropertyValuesHolder implements Cloneable { static class IntPropertyValuesHolder extends PropertyValuesHolder { // Cache JNI functions to avoid looking them up twice - private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap = - new HashMap<Class, HashMap<String, Integer>>(); - int mJniSetter; + private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap = + new HashMap<Class, HashMap<String, Long>>(); + long mJniSetter; private IntProperty mIntProperty; IntKeyframeSet mIntKeyframeSet; @@ -845,11 +845,11 @@ public class PropertyValuesHolder implements Cloneable { // Check new static hashmap<propName, int> for setter method try { mPropertyMapLock.writeLock().lock(); - HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass); + HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); if (propertyMap != null) { - Integer mJniSetterInteger = propertyMap.get(mPropertyName); - if (mJniSetterInteger != null) { - mJniSetter = mJniSetterInteger; + Long jniSetter = propertyMap.get(mPropertyName); + if (jniSetter != null) { + mJniSetter = jniSetter; } } if (mJniSetter == 0) { @@ -857,7 +857,7 @@ public class PropertyValuesHolder implements Cloneable { mJniSetter = nGetIntMethod(targetClass, methodName); if (mJniSetter != 0) { if (propertyMap == null) { - propertyMap = new HashMap<String, Integer>(); + propertyMap = new HashMap<String, Long>(); sJNISetterPropertyMap.put(targetClass, propertyMap); } propertyMap.put(mPropertyName, mJniSetter); @@ -880,9 +880,9 @@ public class PropertyValuesHolder implements Cloneable { static class FloatPropertyValuesHolder extends PropertyValuesHolder { // Cache JNI functions to avoid looking them up twice - private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap = - new HashMap<Class, HashMap<String, Integer>>(); - int mJniSetter; + private static final HashMap<Class, HashMap<String, Long>> sJNISetterPropertyMap = + new HashMap<Class, HashMap<String, Long>>(); + long mJniSetter; private FloatProperty mFloatProperty; FloatKeyframeSet mFloatKeyframeSet; @@ -982,11 +982,11 @@ public class PropertyValuesHolder implements Cloneable { // Check new static hashmap<propName, int> for setter method try { mPropertyMapLock.writeLock().lock(); - HashMap<String, Integer> propertyMap = sJNISetterPropertyMap.get(targetClass); + HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass); if (propertyMap != null) { - Integer mJniSetterInteger = propertyMap.get(mPropertyName); - if (mJniSetterInteger != null) { - mJniSetter = mJniSetterInteger; + Long jniSetter = propertyMap.get(mPropertyName); + if (jniSetter != null) { + mJniSetter = jniSetter; } } if (mJniSetter == 0) { @@ -994,7 +994,7 @@ public class PropertyValuesHolder implements Cloneable { mJniSetter = nGetFloatMethod(targetClass, methodName); if (mJniSetter != 0) { if (propertyMap == null) { - propertyMap = new HashMap<String, Integer>(); + propertyMap = new HashMap<String, Long>(); sJNISetterPropertyMap.put(targetClass, propertyMap); } propertyMap.put(mPropertyName, mJniSetter); @@ -1015,8 +1015,8 @@ public class PropertyValuesHolder implements Cloneable { } - native static private int nGetIntMethod(Class targetClass, String methodName); - native static private int nGetFloatMethod(Class targetClass, String methodName); - native static private void nCallIntMethod(Object target, int methodID, int arg); - native static private void nCallFloatMethod(Object target, int methodID, float arg); -}
\ No newline at end of file + native static private long nGetIntMethod(Class targetClass, String methodName); + native static private long nGetFloatMethod(Class targetClass, String methodName); + native static private void nCallIntMethod(Object target, long methodID, int arg); + native static private void nCallFloatMethod(Object target, long methodID, float arg); +} diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 9a57383..e8b3bb9 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -817,7 +817,7 @@ public class Activity extends ContextThemeWrapper } /** - * Return the LoaderManager for this fragment, creating it if needed. + * Return the LoaderManager for this activity, creating it if needed. */ public LoaderManager getLoaderManager() { if (mLoaderManager != null) { diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 14c495f..e5ee1fa 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -904,7 +904,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM b = data.readStrongBinder(); IUiAutomationConnection c = IUiAutomationConnection.Stub.asInterface(b); int userId = data.readInt(); - boolean res = startInstrumentation(className, profileFile, fl, arguments, w, c, userId); + String abiOverride = data.readString(); + boolean res = startInstrumentation(className, profileFile, fl, arguments, w, c, userId, + abiOverride); reply.writeNoException(); reply.writeInt(res ? 1 : 0); return true; @@ -3168,7 +3170,8 @@ class ActivityManagerProxy implements IActivityManager public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher, - IUiAutomationConnection connection, int userId) throws RemoteException { + IUiAutomationConnection connection, int userId, String instructionSet) + throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); @@ -3179,6 +3182,7 @@ class ActivityManagerProxy implements IActivityManager data.writeStrongBinder(watcher != null ? watcher.asBinder() : null); data.writeStrongBinder(connection != null ? connection.asBinder() : null); data.writeInt(userId); + data.writeString(instructionSet); mRemote.transact(START_INSTRUMENTATION_TRANSACTION, data, reply, 0); reply.readException(); boolean res = reply.readInt() != 0; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b038f9e..156eadb 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -68,6 +68,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; +import android.provider.Settings; import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.DisplayMetrics; @@ -94,6 +95,7 @@ import com.android.internal.os.RuntimeInit; import com.android.internal.os.SamplingProfilerIntegration; import com.android.internal.util.FastPrintWriter; import com.android.org.conscrypt.OpenSSLSocketImpl; +import com.android.org.conscrypt.TrustedCertificateStore; import com.google.android.collect.Lists; import dalvik.system.VMRuntime; @@ -106,6 +108,7 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.net.InetAddress; import java.security.Security; +import java.text.DateFormat; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -742,8 +745,42 @@ public final class ActivityThread { setCoreSettings(coreSettings); - // Tell the VMRuntime about the application. - VMRuntime.registerAppInfo(appInfo.dataDir, appInfo.processName); + /* + * Two possible indications that this package could be + * sharing its runtime with other packages: + * + * 1.) the sharedUserId attribute is set in the manifest, + * indicating a request to share a VM with other + * packages with the same sharedUserId. + * + * 2.) the application element of the manifest has an + * attribute specifying a non-default process name, + * indicating the desire to run in another packages VM. + * + * If sharing is enabled we do not have a unique application + * in a process and therefore cannot rely on the package + * name inside the runtime. + */ + IPackageManager pm = getPackageManager(); + android.content.pm.PackageInfo pi = null; + try { + pi = pm.getPackageInfo(appInfo.packageName, 0, UserHandle.myUserId()); + } catch (RemoteException e) { + } + if (pi != null) { + boolean sharedUserIdSet = (pi.sharedUserId != null); + boolean processNameNotDefault = + (pi.applicationInfo != null && + !appInfo.packageName.equals(pi.applicationInfo.processName)); + boolean sharable = (sharedUserIdSet || processNameNotDefault); + + // Tell the VMRuntime about the application, unless it is shared + // inside a process. + if (!sharable) { + VMRuntime.registerAppInfo(appInfo.packageName, appInfo.dataDir, + appInfo.processName); + } + } AppBindData data = new AppBindData(); data.processName = processName; @@ -1095,6 +1132,11 @@ public final class ActivityThread { public void scheduleInstallProvider(ProviderInfo provider) { sendMessage(H.INSTALL_PROVIDER, provider); } + + @Override + public final void updateTimePrefs(boolean is24Hour) { + DateFormat.set24HourTimePref(is24Hour); + } } private class H extends Handler { @@ -1144,6 +1186,7 @@ public final class ActivityThread { public static final int REQUEST_ASSIST_CONTEXT_EXTRAS = 143; public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144; public static final int INSTALL_PROVIDER = 145; + String codeToString(int code) { if (DEBUG_MESSAGES) { switch (code) { @@ -1541,11 +1584,11 @@ public final class ActivityThread { /** * Creates the top level resources for the given package. */ - Resources getTopLevelResources(String resDir, + Resources getTopLevelResources(String resDir, String[] overlayDirs, int displayId, Configuration overrideConfiguration, LoadedApk pkgInfo) { - return mResourcesManager.getTopLevelResources(resDir, displayId, overrideConfiguration, - pkgInfo.getCompatibilityInfo(), null); + return mResourcesManager.getTopLevelResources(resDir, overlayDirs, displayId, + overrideConfiguration, pkgInfo.getCompatibilityInfo(), null); } final Handler getHandler() { @@ -4186,6 +4229,11 @@ public final class ActivityThread { Log.e(TAG, "Unable to setupGraphicsSupport due to missing cache directory"); } } + + + final boolean is24Hr = "24".equals(mCoreSettings.getString(Settings.System.TIME_12_24)); + DateFormat.set24HourTimePref(is24Hr); + /** * For system applications on userdebug/eng builds, log stack * traces of disk and network access to dropbox for analysis. @@ -5005,6 +5053,10 @@ public final class ActivityThread { Security.addProvider(new AndroidKeyStoreProvider()); + // Make sure TrustedCertificateStore looks in the right place for CA certificates + final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); + TrustedCertificateStore.setDefaultUserDirectory(configDir); + Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 7c30ecd..02192eb 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -826,7 +826,7 @@ final class ApplicationPackageManager extends PackageManager { } Resources r = mContext.mMainThread.getTopLevelResources( app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir, - Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo); + app.resourceDirs, Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo); if (r != null) { return r; } diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 347d43f..cb453e2 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -627,6 +627,15 @@ public abstract class ApplicationThreadNative extends Binder reply.writeNoException(); return true; } + + case UPDATE_TIME_PREFS_TRANSACTION: + { + data.enforceInterface(IApplicationThread.descriptor); + byte is24Hour = data.readByte(); + updateTimePrefs(is24Hour == (byte) 1); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -1266,4 +1275,13 @@ class ApplicationThreadProxy implements IApplicationThread { mRemote.transact(SCHEDULE_INSTALL_PROVIDER_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); } + + @Override + public void updateTimePrefs(boolean is24Hour) throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken(IApplicationThread.descriptor); + data.writeByte(is24Hour ? (byte) 1 : (byte) 0); + mRemote.transact(UPDATE_TIME_PREFS_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); + data.recycle(); + } } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index bb46197..e49fd67 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1628,7 +1628,8 @@ class ContextImpl extends Context { arguments.setAllowFds(false); } return ActivityManagerNative.getDefault().startInstrumentation( - className, profileFile, 0, arguments, null, null, getUserId()); + className, profileFile, 0, arguments, null, null, getUserId(), + null /* ABI override */); } catch (RemoteException e) { // System has crashed, nothing we can do. } @@ -2036,7 +2037,7 @@ class ContextImpl extends Context { || (compatInfo != null && compatInfo.applicationScale != resources.getCompatibilityInfo().applicationScale)) { resources = mResourcesManager.getTopLevelResources( - packageInfo.getResDir(), displayId, + packageInfo.getResDir(), packageInfo.getOverlayDirs(), displayId, overrideConfiguration, compatInfo, activityToken); } } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 4e2a57d..27e97d4 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -171,7 +171,8 @@ public interface IActivityManager extends IInterface { public boolean startInstrumentation(ComponentName className, String profileFile, int flags, Bundle arguments, IInstrumentationWatcher watcher, - IUiAutomationConnection connection, int userId) throws RemoteException; + IUiAutomationConnection connection, int userId, + String abiOverride) throws RemoteException; public void finishInstrumentation(IApplicationThread target, int resultCode, Bundle results) throws RemoteException; diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl index 8476609..ef9f26e 100644 --- a/core/java/android/app/IAlarmManager.aidl +++ b/core/java/android/app/IAlarmManager.aidl @@ -28,7 +28,7 @@ interface IAlarmManager { /** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */ void set(int type, long triggerAtTime, long windowLength, long interval, in PendingIntent operation, in WorkSource workSource); - void setTime(long millis); + boolean setTime(long millis); void setTimeZone(String zone); void remove(in PendingIntent operation); } diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index d0cc1bb..3aceff9 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -138,6 +138,7 @@ public interface IApplicationThread extends IInterface { throws RemoteException; void setProcessState(int state) throws RemoteException; void scheduleInstallProvider(ProviderInfo provider) throws RemoteException; + void updateTimePrefs(boolean is24Hour) throws RemoteException; String descriptor = "android.app.IApplicationThread"; @@ -191,4 +192,5 @@ public interface IApplicationThread extends IInterface { int SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+48; int SET_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+49; int SCHEDULE_INSTALL_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+50; + int UPDATE_TIME_PREFS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+51; } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 8b0fd87..d409352 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -76,6 +76,7 @@ public final class LoadedApk { final String mPackageName; private final String mAppDir; private final String mResDir; + private final String[] mOverlayDirs; private final String[] mSharedLibraries; private final String mDataDir; private final String mLibDir; @@ -119,6 +120,7 @@ public final class LoadedApk { final int myUid = Process.myUid(); mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir; + mOverlayDirs = aInfo.resourceDirs; if (!UserHandle.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) { aInfo.dataDir = PackageManager.getDataDirForUser(UserHandle.getUserId(myUid), mPackageName); @@ -144,6 +146,7 @@ public final class LoadedApk { mPackageName = "android"; mAppDir = null; mResDir = null; + mOverlayDirs = null; mSharedLibraries = null; mDataDir = null; mDataDirFile = null; @@ -463,6 +466,10 @@ public final class LoadedApk { return mResDir; } + public String[] getOverlayDirs() { + return mOverlayDirs; + } + public String getDataDir() { return mDataDir; } @@ -477,7 +484,7 @@ public final class LoadedApk { public Resources getResources(ActivityThread mainThread) { if (mResources == null) { - mResources = mainThread.getTopLevelResources(mResDir, + mResources = mainThread.getTopLevelResources(mResDir, mOverlayDirs, Display.DEFAULT_DISPLAY, null, this); } return mResources; diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index f55dba4..728f372 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -147,7 +147,7 @@ public class ResourcesManager { * @param compatInfo the compability info. Must not be null. * @param token the application token for determining stack bounds. */ - public Resources getTopLevelResources(String resDir, int displayId, + public Resources getTopLevelResources(String resDir, String[] overlayDirs, int displayId, Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) { final float scale = compatInfo.applicationScale; ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale, @@ -180,6 +180,12 @@ public class ResourcesManager { return null; } + if (overlayDirs != null) { + for (String idmapPath : overlayDirs) { + assets.addOverlayPath(idmapPath); + } + } + //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics); DisplayMetrics dm = getDisplayMetricsLocked(displayId); Configuration config; diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java index a292ecb..a8896c2 100644 --- a/core/java/android/app/SharedPreferencesImpl.java +++ b/core/java/android/app/SharedPreferencesImpl.java @@ -19,6 +19,9 @@ package android.app; import android.content.SharedPreferences; import android.os.FileUtils; import android.os.Looper; +import android.system.ErrnoException; +import android.system.Os; +import android.system.StructStat; import android.util.Log; import com.google.android.collect.Maps; @@ -44,10 +47,7 @@ import java.util.WeakHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; -import libcore.io.ErrnoException; import libcore.io.IoUtils; -import libcore.io.Libcore; -import libcore.io.StructStat; final class SharedPreferencesImpl implements SharedPreferences { private static final String TAG = "SharedPreferencesImpl"; @@ -111,7 +111,7 @@ final class SharedPreferencesImpl implements SharedPreferences { Map map = null; StructStat stat = null; try { - stat = Libcore.os.stat(mFile.getPath()); + stat = Os.stat(mFile.getPath()); if (mFile.canRead()) { BufferedInputStream str = null; try { @@ -173,7 +173,7 @@ final class SharedPreferencesImpl implements SharedPreferences { * violation, but we explicitly want this one. */ BlockGuard.getThreadPolicy().onReadFromDisk(); - stat = Libcore.os.stat(mFile.getPath()); + stat = Os.stat(mFile.getPath()); } catch (ErrnoException e) { return true; } @@ -600,7 +600,7 @@ final class SharedPreferencesImpl implements SharedPreferences { str.close(); ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0); try { - final StructStat stat = Libcore.os.stat(mFile.getPath()); + final StructStat stat = Os.stat(mFile.getPath()); synchronized (this) { mStatTimestamp = stat.st_mtime; mStatSize = stat.st_size; diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index e16ae7a..f87001a 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -45,6 +45,7 @@ import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.util.Log; import android.view.WindowManagerGlobal; @@ -659,6 +660,10 @@ public class WallpaperManager { * not "image/*" */ public Intent getCropAndSetWallpaperIntent(Uri imageUri) { + if (imageUri == null) { + throw new IllegalArgumentException("Image URI must not be null"); + } + if (!ContentResolver.SCHEME_CONTENT.equals(imageUri.getScheme())) { throw new IllegalArgumentException("Image URI must be of the " + ContentResolver.SCHEME_CONTENT + " scheme type"); @@ -915,6 +920,35 @@ public class WallpaperManager { */ public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) { try { + /** + * The framework makes no attempt to limit the window size + * to the maximum texture size. Any window larger than this + * cannot be composited. + * + * Read maximum texture size from system property and scale down + * minimumWidth and minimumHeight accordingly. + */ + int maximumTextureSize; + try { + maximumTextureSize = SystemProperties.getInt("sys.max_texture_size", 0); + } catch (Exception e) { + maximumTextureSize = 0; + } + + if (maximumTextureSize > 0) { + if ((minimumWidth > maximumTextureSize) || + (minimumHeight > maximumTextureSize)) { + float aspect = (float)minimumHeight / (float)minimumWidth; + if (minimumWidth > minimumHeight) { + minimumWidth = maximumTextureSize; + minimumHeight = (int)((minimumWidth * aspect) + 0.5); + } else { + minimumHeight = maximumTextureSize; + minimumWidth = (int)((minimumHeight / aspect) + 0.5); + } + } + } + if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); } else { diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 67c772b..70a3797 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -29,6 +29,10 @@ import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; +import android.system.StructStat; import android.util.Log; import java.io.File; @@ -38,11 +42,6 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.concurrent.CountDownLatch; -import libcore.io.ErrnoException; -import libcore.io.Libcore; -import libcore.io.OsConstants; -import libcore.io.StructStat; - /** * Provides the central interface between an * application and Android's data backup infrastructure. An application that wishes @@ -191,7 +190,7 @@ public abstract class BackupAgent extends ContextWrapper { * the key supplied as part of the entity. Writing an entity with a negative * data size instructs the transport to delete whatever entity currently exists * under that key from the remote data set. - * + * * @param oldState An open, read-only ParcelFileDescriptor pointing to the * last backup state provided by the application. May be * <code>null</code>, in which case no prior state is being @@ -222,7 +221,7 @@ public abstract class BackupAgent extends ContextWrapper { * onRestore() throws an exception, the OS will assume that the * application's data may now be in an incoherent state, and will clear it * before proceeding. - * + * * @param data A structured wrapper around an open, read-only * file descriptor pointing to a full snapshot of the * application's data. The application should consume every @@ -412,7 +411,7 @@ public abstract class BackupAgent extends ContextWrapper { } // If it's a directory, enqueue its contents for scanning. - StructStat stat = Libcore.os.lstat(filePath); + StructStat stat = Os.lstat(filePath); if (OsConstants.S_ISLNK(stat.st_mode)) { if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file); continue; diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index cb0737e..477285d 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -20,6 +20,8 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.ParcelFileDescriptor; +import android.system.ErrnoException; +import android.system.Os; import android.util.Log; import java.io.File; @@ -27,9 +29,6 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import libcore.io.ErrnoException; -import libcore.io.Libcore; - /** * Global constant definitions et cetera related to the full-backup-to-fd * binary format. Nothing in this namespace is part of any API; it's all @@ -150,7 +149,7 @@ public class FullBackup { try { // explicitly prevent emplacement of files accessible by outside apps mode &= 0700; - Libcore.os.chmod(outFile.getPath(), (int)mode); + Os.chmod(outFile.getPath(), (int)mode); } catch (ErrnoException e) { e.rethrowAsIOException(); } diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 9d0ab3a..02c850b 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -987,7 +987,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * Implement this to handle requests to delete one or more rows. * The implementation should apply the selection clause when performing * deletion, allowing the operation to affect multiple rows in a directory. - * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyDelete()} + * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} * after deleting. * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 228a97d..b1a743b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3361,6 +3361,15 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY"; + /** + * Optional boolean extra for {@link #ACTION_TIME_CHANGED} that indicates the + * user has set their time format preferences to the 24 hour format. + * + * @hide for internal use only. + */ + public static final String EXTRA_TIME_PREF_24_HOUR_FORMAT = + "android.intent.extra.TIME_PREF_24_HOUR_FORMAT"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Intent flags (see mFlags variable). diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java index 911e49c..a045b3a 100644 --- a/core/java/android/content/Loader.java +++ b/core/java/android/content/Loader.java @@ -24,7 +24,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; /** - * An abstract class that performs asynchronous loading of data. While Loaders are active + * A class that performs asynchronous loading of data. While Loaders are active * they should monitor the source of their data and deliver new results when the contents * change. See {@link android.app.LoaderManager} for more detail. * diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index b22c8a5..2639625 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -449,6 +449,15 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 cpuAbi; + + /** * 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). @@ -578,6 +587,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { sourceDir = orig.sourceDir; publicSourceDir = orig.publicSourceDir; nativeLibraryDir = orig.nativeLibraryDir; + cpuAbi = orig.cpuAbi; resourceDirs = orig.resourceDirs; seinfo = orig.seinfo; sharedLibraryFiles = orig.sharedLibraryFiles; @@ -618,6 +628,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeString(sourceDir); dest.writeString(publicSourceDir); dest.writeString(nativeLibraryDir); + dest.writeString(cpuAbi); dest.writeStringArray(resourceDirs); dest.writeString(seinfo); dest.writeStringArray(sharedLibraryFiles); @@ -657,6 +668,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { sourceDir = source.readString(); publicSourceDir = source.readString(); nativeLibraryDir = source.readString(); + cpuAbi = source.readString(); resourceDirs = source.readStringArray(); seinfo = source.readString(); sharedLibraryFiles = source.readStringArray(); diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 20002ad..3163951 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -402,6 +402,12 @@ interface IPackageManager { in VerificationParams verificationParams, in ContainerEncryptionParams encryptionParams); + void installPackageWithVerificationEncryptionAndAbiOverride(in Uri packageURI, + in IPackageInstallObserver observer, int flags, in String installerPackageName, + in VerificationParams verificationParams, + in ContainerEncryptionParams encryptionParams, + in String packageAbiOverride); + int installExistingPackageAsUser(String packageName, int userId); void verifyPendingInstall(int id, int verificationCode); diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index af1a6d5..785f2b4 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -227,6 +227,14 @@ public class PackageInfo implements Parcelable { /** @hide */ public String requiredAccountType; + /** + * What package, if any, this package will overlay. + * + * Package name of target package, or null. + * @hide + */ + public String overlayTarget; + public PackageInfo() { } @@ -270,6 +278,7 @@ public class PackageInfo implements Parcelable { dest.writeInt(requiredForAllUsers ? 1 : 0); dest.writeString(restrictedAccountType); dest.writeString(requiredAccountType); + dest.writeString(overlayTarget); } public static final Parcelable.Creator<PackageInfo> CREATOR @@ -311,5 +320,6 @@ public class PackageInfo implements Parcelable { requiredForAllUsers = source.readInt() != 0; restrictedAccountType = source.readString(); requiredAccountType = source.readString(); + overlayTarget = source.readString(); } } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index ff88dd7..99d047d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -494,7 +494,7 @@ public abstract class PackageManager { * Installation return code: this is passed to the {@link IPackageInstallObserver} by * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if * the package being installed contains native code, but none that is - * compatible with the the device's CPU_ABI. + * compatible with the device's CPU_ABI. * @hide */ public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16; @@ -675,6 +675,25 @@ public abstract class PackageManager { 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. * @@ -3243,7 +3262,7 @@ public abstract class PackageManager { /** - * Return the the enabled setting for a package component (activity, + * Return the enabled setting for a package component (activity, * receiver, service, provider). This returns the last value set by * {@link #setComponentEnabledSetting(ComponentName, int, int)}; in most * cases this value will be {@link #COMPONENT_ENABLED_STATE_DEFAULT} since @@ -3281,14 +3300,14 @@ public abstract class PackageManager { int newState, int flags); /** - * Return the the enabled setting for an application. This returns + * Return the enabled setting for an application. This returns * the last value set by * {@link #setApplicationEnabledSetting(String, int, int)}; in most * cases this value will be {@link #COMPONENT_ENABLED_STATE_DEFAULT} since * the value originally specified in the manifest has not been modified. * - * @param packageName The component to retrieve. - * @return Returns the current enabled state for the component. May + * @param packageName The package name of the application to retrieve. + * @return Returns the current enabled state for the application. May * be one of {@link #COMPONENT_ENABLED_STATE_ENABLED}, * {@link #COMPONENT_ENABLED_STATE_DISABLED}, or * {@link #COMPONENT_ENABLED_STATE_DEFAULT}. The last one means the diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index d5de97a..66b2bb2 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -309,6 +309,7 @@ public class PackageParser { } pi.restrictedAccountType = p.mRestrictedAccountType; pi.requiredAccountType = p.mRequiredAccountType; + pi.overlayTarget = p.mOverlayTarget; pi.firstInstallTime = firstInstallTime; pi.lastUpdateTime = lastUpdateTime; if ((flags&PackageManager.GET_GIDS) != 0) { @@ -492,6 +493,11 @@ public class PackageParser { public Package parsePackage(File sourceFile, String destCodePath, DisplayMetrics metrics, int flags) { + return parsePackage(sourceFile, destCodePath, metrics, flags, false); + } + + public Package parsePackage(File sourceFile, String destCodePath, + DisplayMetrics metrics, int flags, boolean trustedOverlay) { mParseError = PackageManager.INSTALL_SUCCEEDED; mArchiveSourcePath = sourceFile.getPath(); @@ -544,7 +550,7 @@ public class PackageParser { Exception errorException = null; try { // XXXX todo: need to figure out correct configuration. - pkg = parsePackage(res, parser, flags, errorText); + pkg = parsePackage(res, parser, flags, trustedOverlay, errorText); } catch (Exception e) { errorException = e; mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; @@ -953,8 +959,8 @@ public class PackageParser { } private Package parsePackage( - Resources res, XmlResourceParser parser, int flags, String[] outError) - throws XmlPullParserException, IOException { + Resources res, XmlResourceParser parser, int flags, boolean trustedOverlay, + String[] outError) throws XmlPullParserException, IOException { AttributeSet attrs = parser; mParseInstrumentationArgs = null; @@ -1053,6 +1059,31 @@ public class PackageParser { if (!parseApplication(pkg, res, parser, attrs, flags, outError)) { return null; } + } else if (tagName.equals("overlay")) { + pkg.mTrustedOverlay = trustedOverlay; + + sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestResourceOverlay); + pkg.mOverlayTarget = sa.getString( + com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage); + pkg.mOverlayPriority = sa.getInt( + com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority, + -1); + sa.recycle(); + + if (pkg.mOverlayTarget == null) { + outError[0] = "<overlay> does not specify a target package"; + mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; + return null; + } + if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) { + outError[0] = "<overlay> priority must be between 0 and 9999"; + mParseError = + PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; + return null; + } + XmlUtils.skipCurrentTag(parser); + } else if (tagName.equals("keys")) { if (!parseKeys(pkg, res, parser, attrs, outError)) { return null; @@ -3534,10 +3565,13 @@ public class PackageParser { // For use by the package manager to keep track of the path to the // file an app came from. public String mScanPath; - - // For use by package manager to keep track of where it has done dexopt. - public boolean mDidDexOpt; - + + // For use by package manager to keep track of where it needs to do dexopt. + public boolean mDexOptNeeded = true; + + // For use by package manager to keep track of when a package was last used. + public long mLastPackageUsageTimeInMills; + // // User set enabled state. // public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; // @@ -3578,6 +3612,10 @@ public class PackageParser { */ public ManifestDigest manifestDigest; + public String mOverlayTarget; + public int mOverlayPriority; + public boolean mTrustedOverlay; + /** * Data used to feed the KeySetManager */ diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 93ce633..9ce17e4 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -69,13 +69,13 @@ public final class AssetManager { private final long[] mOffsets = new long[2]; // For communication with native code. - private int mObject; + private long mObject; private StringBlock mStringBlocks[] = null; private int mNumRefs = 1; private boolean mOpen = true; - private HashMap<Integer, RuntimeException> mRefStacks; + private HashMap<Long, RuntimeException> mRefStacks; /** * Create a new AssetManager containing only the basic system assets. @@ -90,7 +90,7 @@ public final class AssetManager { mNumRefs = 0; incRefsLocked(this.hashCode()); } - init(); + init(false); if (localLOGV) Log.v(TAG, "New asset manager: " + this); ensureSystemAssets(); } @@ -113,7 +113,7 @@ public final class AssetManager { incRefsLocked(this.hashCode()); } } - init(); + init(true); if (localLOGV) Log.v(TAG, "New asset manager: " + this); } @@ -224,7 +224,7 @@ public final class AssetManager { return retArray; } - /*package*/ final boolean getThemeValue(int theme, int ident, + /*package*/ final boolean getThemeValue(long theme, int ident, TypedValue outValue, boolean resolveRefs) { int block = loadThemeAttributeValue(theme, ident, outValue, resolveRefs); if (block >= 0) { @@ -312,7 +312,7 @@ public final class AssetManager { if (!mOpen) { throw new RuntimeException("Assetmanager has been closed"); } - int asset = openAsset(fileName, accessMode); + long asset = openAsset(fileName, accessMode); if (asset != 0) { AssetInputStream res = new AssetInputStream(asset); incRefsLocked(res.hashCode()); @@ -404,7 +404,7 @@ public final class AssetManager { if (!mOpen) { throw new RuntimeException("Assetmanager has been closed"); } - int asset = openNonAssetNative(cookie, fileName, accessMode); + long asset = openNonAssetNative(cookie, fileName, accessMode); if (asset != 0) { AssetInputStream res = new AssetInputStream(asset); incRefsLocked(res.hashCode()); @@ -484,7 +484,7 @@ public final class AssetManager { if (!mOpen) { throw new RuntimeException("Assetmanager has been closed"); } - int xmlBlock = openXmlAssetNative(cookie, fileName); + long xmlBlock = openXmlAssetNative(cookie, fileName); if (xmlBlock != 0) { XmlBlock res = new XmlBlock(this, xmlBlock); incRefsLocked(res.hashCode()); @@ -500,18 +500,18 @@ public final class AssetManager { } } - /*package*/ final int createTheme() { + /*package*/ final long createTheme() { synchronized (this) { if (!mOpen) { throw new RuntimeException("Assetmanager has been closed"); } - int res = newTheme(); + long res = newTheme(); incRefsLocked(res); return res; } } - /*package*/ final void releaseTheme(int theme) { + /*package*/ final void releaseTheme(long theme) { synchronized (this) { deleteTheme(theme); decRefsLocked(theme); @@ -537,7 +537,7 @@ public final class AssetManager { public final class AssetInputStream extends InputStream { public final int getAssetInt() { - return mAsset; + throw new UnsupportedOperationException(); } /** * @hide @@ -545,7 +545,7 @@ public final class AssetManager { public final long getNativeAsset() { return mAsset; } - private AssetInputStream(int asset) + private AssetInputStream(long asset) { mAsset = asset; mLength = getAssetLength(asset); @@ -597,7 +597,7 @@ public final class AssetManager { close(); } - private int mAsset; + private long mAsset; private long mLength; private long mMarkPos; } @@ -615,6 +615,16 @@ public final class AssetManager { private native final int addAssetPathNative(String path); + /** + * Add a set of assets to overlay an already added set of assets. + * + * This is only intended for application resources. System wide resources + * are handled before any Java code is executed. + * + * {@hide} + */ + public native final int addOverlayPath(String idmapPath); + /** * Add multiple sets of assets to the asset manager at once. See * {@link #addAssetPath(String)} for more information. Returns array of @@ -678,19 +688,19 @@ public final class AssetManager { /*package*/ native final String getResourceTypeName(int resid); /*package*/ native final String getResourceEntryName(int resid); - private native final int openAsset(String fileName, int accessMode); + private native final long openAsset(String fileName, int accessMode); private final native ParcelFileDescriptor openAssetFd(String fileName, long[] outOffsets) throws IOException; - private native final int openNonAssetNative(int cookie, String fileName, + private native final long openNonAssetNative(int cookie, String fileName, int accessMode); private native ParcelFileDescriptor openNonAssetFdNative(int cookie, String fileName, long[] outOffsets) throws IOException; - private native final void destroyAsset(int asset); - private native final int readAssetChar(int asset); - private native final int readAsset(int asset, byte[] b, int off, int len); - private native final long seekAsset(int asset, long offset, int whence); - private native final long getAssetLength(int asset); - private native final long getAssetRemainingLength(int asset); + private native final void destroyAsset(long asset); + private native final int readAssetChar(long asset); + private native final int readAsset(long asset, byte[] b, int off, int len); + private native final long seekAsset(long asset, long offset, int whence); + private native final long getAssetLength(long asset); + private native final long getAssetRemainingLength(long asset); /** Returns true if the resource was found, filling in mRetStringBlock and * mRetData. */ @@ -707,15 +717,15 @@ public final class AssetManager { /*package*/ static final int STYLE_RESOURCE_ID = 3; /*package*/ static final int STYLE_CHANGING_CONFIGURATIONS = 4; /*package*/ static final int STYLE_DENSITY = 5; - /*package*/ native static final boolean applyStyle(int theme, - int defStyleAttr, int defStyleRes, int xmlParser, + /*package*/ native static final boolean applyStyle(long theme, + int defStyleAttr, int defStyleRes, long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); /*package*/ native final boolean retrieveAttributes( - int xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); + long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); /*package*/ native final int getArraySize(int resource); /*package*/ native final int retrieveArray(int resource, int[] outValues); private native final int getStringBlockCount(); - private native final int getNativeStringBlock(int block); + private native final long getNativeStringBlock(int block); /** * {@hide} @@ -737,37 +747,37 @@ public final class AssetManager { */ public native static final int getGlobalAssetManagerCount(); - private native final int newTheme(); - private native final void deleteTheme(int theme); - /*package*/ native static final void applyThemeStyle(int theme, int styleRes, boolean force); - /*package*/ native static final void copyTheme(int dest, int source); - /*package*/ native static final int loadThemeAttributeValue(int theme, int ident, + private native final long newTheme(); + private native final void deleteTheme(long theme); + /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force); + /*package*/ native static final void copyTheme(long dest, long source); + /*package*/ native static final int loadThemeAttributeValue(long theme, int ident, TypedValue outValue, boolean resolve); - /*package*/ native static final void dumpTheme(int theme, int priority, String tag, String prefix); + /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix); - private native final int openXmlAssetNative(int cookie, String fileName); + private native final long openXmlAssetNative(int cookie, String fileName); private native final String[] getArrayStringResource(int arrayRes); private native final int[] getArrayStringInfo(int arrayRes); /*package*/ native final int[] getArrayIntResource(int arrayRes); - private native final void init(); + private native final void init(boolean isSystem); private native final void destroy(); - private final void incRefsLocked(int id) { + private final void incRefsLocked(long id) { if (DEBUG_REFS) { if (mRefStacks == null) { - mRefStacks = new HashMap<Integer, RuntimeException>(); - RuntimeException ex = new RuntimeException(); - ex.fillInStackTrace(); - mRefStacks.put(this.hashCode(), ex); + mRefStacks = new HashMap<Long, RuntimeException>(); } + RuntimeException ex = new RuntimeException(); + ex.fillInStackTrace(); + mRefStacks.put(id, ex); } mNumRefs++; } - private final void decRefsLocked(int id) { + private final void decRefsLocked(long id) { if (DEBUG_REFS && mRefStacks != null) { mRefStacks.remove(id); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 02e1761..7318652 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1460,10 +1460,10 @@ public class Resources { } private final AssetManager mAssets; - private final int mTheme; + private final long mTheme; // Needed by layoutlib. - /*package*/ int getNativeTheme() { + /*package*/ long getNativeTheme() { return mTheme; } } @@ -1574,10 +1574,7 @@ public class Resources { String locale = null; if (mConfiguration.locale != null) { - locale = mConfiguration.locale.getLanguage(); - if (mConfiguration.locale.getCountry() != null) { - locale += "-" + mConfiguration.locale.getCountry(); - } + locale = mConfiguration.locale.toLanguageTag(); } int width, height; if (mMetrics.widthPixels >= mMetrics.heightPixels) { diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java index 78180b1..77b8a33 100644 --- a/core/java/android/content/res/StringBlock.java +++ b/core/java/android/content/res/StringBlock.java @@ -36,7 +36,7 @@ final class StringBlock { private static final String TAG = "AssetManager"; private static final boolean localLOGV = false; - private final int mNative; + private final long mNative; private final boolean mUseSparse; private final boolean mOwnsNative; private CharSequence[] mStrings; @@ -474,7 +474,7 @@ final class StringBlock { * are doing! The given native object must exist for the entire lifetime * of this newly creating StringBlock. */ - StringBlock(int obj, boolean useSparse) { + StringBlock(long obj, boolean useSparse) { mNative = obj; mUseSparse = useSparse; mOwnsNative = false; @@ -482,11 +482,11 @@ final class StringBlock { + ": " + nativeGetSize(mNative)); } - private static native int nativeCreate(byte[] data, + private static native long nativeCreate(byte[] data, int offset, int size); - private static native int nativeGetSize(int obj); - private static native String nativeGetString(int obj, int idx); - private static native int[] nativeGetStyle(int obj, int idx); - private static native void nativeDestroy(int obj); + private static native int nativeGetSize(long obj); + private static native String nativeGetString(long obj, int idx); + private static native int[] nativeGetStyle(long obj, int idx); + private static native void nativeDestroy(long obj); } diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index bea6529..3ad357f 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -75,7 +75,7 @@ final class XmlBlock { } /*package*/ final class Parser implements XmlResourceParser { - Parser(int parseState, XmlBlock block) { + Parser(long parseState, XmlBlock block) { mParseState = parseState; mBlock = block; block.mOpenCount++; @@ -458,7 +458,7 @@ final class XmlBlock { return mStrings.get(id); } - /*package*/ int mParseState; + /*package*/ long mParseState; private final XmlBlock mBlock; private boolean mStarted = false; private boolean mDecNextDepth = false; @@ -476,41 +476,41 @@ final class XmlBlock { * are doing! The given native object must exist for the entire lifetime * of this newly creating XmlBlock. */ - XmlBlock(AssetManager assets, int xmlBlock) { + XmlBlock(AssetManager assets, long xmlBlock) { mAssets = assets; mNative = xmlBlock; mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false); } private final AssetManager mAssets; - private final int mNative; + private final long mNative; /*package*/ final StringBlock mStrings; private boolean mOpen = true; private int mOpenCount = 1; - private static final native int nativeCreate(byte[] data, + private static final native long nativeCreate(byte[] data, int offset, int size); - private static final native int nativeGetStringBlock(int obj); + private static final native long nativeGetStringBlock(long obj); - private static final native int nativeCreateParseState(int obj); - /*package*/ static final native int nativeNext(int state); - private static final native int nativeGetNamespace(int state); - /*package*/ static final native int nativeGetName(int state); - private static final native int nativeGetText(int state); - private static final native int nativeGetLineNumber(int state); - private static final native int nativeGetAttributeCount(int state); - private static final native int nativeGetAttributeNamespace(int state, int idx); - private static final native int nativeGetAttributeName(int state, int idx); - private static final native int nativeGetAttributeResource(int state, int idx); - private static final native int nativeGetAttributeDataType(int state, int idx); - private static final native int nativeGetAttributeData(int state, int idx); - private static final native int nativeGetAttributeStringValue(int state, int idx); - private static final native int nativeGetIdAttribute(int state); - private static final native int nativeGetClassAttribute(int state); - private static final native int nativeGetStyleAttribute(int state); - private static final native int nativeGetAttributeIndex(int state, String namespace, String name); - private static final native void nativeDestroyParseState(int state); + private static final native long nativeCreateParseState(long obj); + /*package*/ static final native int nativeNext(long state); + private static final native int nativeGetNamespace(long state); + /*package*/ static final native int nativeGetName(long state); + private static final native int nativeGetText(long state); + private static final native int nativeGetLineNumber(long state); + private static final native int nativeGetAttributeCount(long state); + private static final native int nativeGetAttributeNamespace(long state, int idx); + private static final native int nativeGetAttributeName(long state, int idx); + private static final native int nativeGetAttributeResource(long state, int idx); + private static final native int nativeGetAttributeDataType(long state, int idx); + private static final native int nativeGetAttributeData(long state, int idx); + private static final native int nativeGetAttributeStringValue(long state, int idx); + private static final native int nativeGetIdAttribute(long state); + private static final native int nativeGetClassAttribute(long state); + private static final native int nativeGetStyleAttribute(long state); + private static final native int nativeGetAttributeIndex(long state, String namespace, String name); + private static final native void nativeDestroyParseState(long state); - private static final native void nativeDestroy(int obj); + private static final native void nativeDestroy(long obj); } diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index 197e3ff..a75372f 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -42,12 +42,8 @@ import android.util.LongSparseArray; public class CursorWindow extends SQLiteClosable implements Parcelable { private static final String STATS_TAG = "CursorWindowStats"; - /** The cursor window size. resource xml file specifies the value in kB. - * convert it to bytes here by multiplying with 1024. - */ - private static final int sCursorWindowSize = - Resources.getSystem().getInteger( - com.android.internal.R.integer.config_cursorWindowSize) * 1024; + // This static member will be evaluated when first used. + private static int sCursorWindowSize = -1; /** * The native CursorWindow object pointer. (FOR INTERNAL USE ONLY) @@ -100,6 +96,13 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { public CursorWindow(String name) { mStartPos = 0; mName = name != null && name.length() != 0 ? name : "<unnamed>"; + if (sCursorWindowSize < 0) { + /** The cursor window size. resource xml file specifies the value in kB. + * convert it to bytes here by multiplying with 1024. + */ + sCursorWindowSize = Resources.getSystem().getInteger( + com.android.internal.R.integer.config_cursorWindowSize) * 1024; + } mWindowPtr = nativeCreate(mName, sCursorWindowSize); if (mWindowPtr == 0) { throw new CursorWindowAllocationException("Cursor window allocation of " + diff --git a/core/java/android/ddm/DdmHandleHeap.java b/core/java/android/ddm/DdmHandleHeap.java index cece556..e24aeb2 100644 --- a/core/java/android/ddm/DdmHandleHeap.java +++ b/core/java/android/ddm/DdmHandleHeap.java @@ -219,7 +219,7 @@ public class DdmHandleHeap extends ChunkHandler { if (false) Log.d("ddm-heap", "Heap GC request"); - System.gc(); + Runtime.getRuntime().gc(); return null; // empty response } diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java index 220b40d..2dce425 100644 --- a/core/java/android/ddm/DdmHandleHello.java +++ b/core/java/android/ddm/DdmHandleHello.java @@ -22,6 +22,7 @@ import org.apache.harmony.dalvik.ddmc.DdmServer; import android.util.Log; import android.os.Debug; import android.os.UserHandle; +import dalvik.system.VMRuntime; import java.nio.ByteBuffer; @@ -126,8 +127,21 @@ public class DdmHandleHello extends ChunkHandler { // appName = "unknown"; String appName = DdmHandleAppName.getAppName(); - ByteBuffer out = ByteBuffer.allocate(20 - + vmIdent.length()*2 + appName.length()*2); + VMRuntime vmRuntime = VMRuntime.getRuntime(); + String instructionSetDescription = + vmRuntime.is64Bit() ? "64-bit" : "32-bit"; + String vmInstructionSet = vmRuntime.vmInstructionSet(); + if (vmInstructionSet != null && vmInstructionSet.length() > 0) { + instructionSetDescription += " (" + vmInstructionSet + ")"; + } + String vmFlags = "CheckJNI=" + + (vmRuntime.isCheckJniEnabled() ? "true" : "false"); + + ByteBuffer out = ByteBuffer.allocate(28 + + vmIdent.length() * 2 + + appName.length() * 2 + + instructionSetDescription.length() * 2 + + vmFlags.length() * 2); out.order(ChunkHandler.CHUNK_ORDER); out.putInt(DdmServer.CLIENT_PROTOCOL_VERSION); out.putInt(android.os.Process.myPid()); @@ -136,6 +150,10 @@ public class DdmHandleHello extends ChunkHandler { putString(out, vmIdent); putString(out, appName); out.putInt(UserHandle.myUserId()); + out.putInt(instructionSetDescription.length()); + putString(out, instructionSetDescription); + out.putInt(vmFlags.length()); + putString(out, vmFlags); Chunk reply = new Chunk(CHUNK_HELO, out); diff --git a/core/java/android/debug/JNITest.java b/core/java/android/debug/JNITest.java deleted file mode 100644 index 2ce374a..0000000 --- a/core/java/android/debug/JNITest.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2006 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.debug; - -/** - * Simple JNI verification test. - */ -public class JNITest { - - public JNITest() { - } - - public int test(int intArg, double doubleArg, String stringArg) { - int[] intArray = { 42, 53, 65, 127 }; - - return part1(intArg, doubleArg, stringArg, intArray); - } - - private native int part1(int intArg, double doubleArg, String stringArg, - int[] arrayArg); - - private int part2(double doubleArg, int fromArray, String stringArg) { - int result; - - System.out.println(stringArg + " : " + (float) doubleArg + " : " + - fromArray); - result = part3(stringArg); - - return result + 6; - } - - private static native int part3(String stringArg); -} - diff --git a/core/java/android/emoji/EmojiFactory.java b/core/java/android/emoji/EmojiFactory.java index 8fd8695..aba990d 100644 --- a/core/java/android/emoji/EmojiFactory.java +++ b/core/java/android/emoji/EmojiFactory.java @@ -54,7 +54,7 @@ public final class EmojiFactory { } // A pointer to native EmojiFactory object. - private int mNativeEmojiFactory; + private long mNativeEmojiFactory; private String mName; // Cache. private Map<Integer, WeakReference<Bitmap>> mCache; @@ -68,7 +68,7 @@ public final class EmojiFactory { * * This can be called from JNI code. */ - private EmojiFactory(int nativeEmojiFactory, String name) { + private EmojiFactory(long nativeEmojiFactory, String name) { mNativeEmojiFactory = nativeEmojiFactory; mName = name; mCache = new CustomLinkedHashMap<Integer, WeakReference<Bitmap>>(); @@ -272,18 +272,18 @@ public final class EmojiFactory { // native methods - private native void nativeDestructor(int factory); - private native Bitmap nativeGetBitmapFromAndroidPua(int nativeEmojiFactory, int AndroidPua); - private native int nativeGetAndroidPuaFromVendorSpecificSjis(int nativeEmojiFactory, + private native void nativeDestructor(long nativeEmojiFactory); + private native Bitmap nativeGetBitmapFromAndroidPua(long nativeEmojiFactory, int AndroidPua); + private native int nativeGetAndroidPuaFromVendorSpecificSjis(long nativeEmojiFactory, char sjis); - private native int nativeGetVendorSpecificSjisFromAndroidPua(int nativeEmojiFactory, + private native int nativeGetVendorSpecificSjisFromAndroidPua(long nativeEmojiFactory, int pua); - private native int nativeGetAndroidPuaFromVendorSpecificPua(int nativeEmojiFactory, + private native int nativeGetAndroidPuaFromVendorSpecificPua(long nativeEmojiFactory, int vsp); - private native int nativeGetVendorSpecificPuaFromAndroidPua(int nativeEmojiFactory, + private native int nativeGetVendorSpecificPuaFromAndroidPua(long nativeEmojiFactory, int pua); - private native int nativeGetMaximumVendorSpecificPua(int nativeEmojiFactory); - private native int nativeGetMinimumVendorSpecificPua(int nativeEmojiFactory); - private native int nativeGetMaximumAndroidPua(int nativeEmojiFactory); - private native int nativeGetMinimumAndroidPua(int nativeEmojiFactory); + private native int nativeGetMaximumVendorSpecificPua(long nativeEmojiFactory); + private native int nativeGetMinimumVendorSpecificPua(long nativeEmojiFactory); + private native int nativeGetMaximumAndroidPua(long nativeEmojiFactory); + private native int nativeGetMinimumAndroidPua(long nativeEmojiFactory); } diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index b9c9d35..25c7630 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -1367,7 +1367,7 @@ public abstract class SensorManager { float q2 = rotationVector[1]; float q3 = rotationVector[2]; - if (rotationVector.length == 4) { + if (rotationVector.length >= 4) { q0 = rotationVector[3]; } else { q0 = 1 - q1*q1 - q2*q2 - q3*q3; @@ -1424,7 +1424,7 @@ public abstract class SensorManager { * @param Q an array of floats in which to store the computed quaternion */ public static void getQuaternionFromVector(float[] Q, float[] rv) { - if (rv.length == 4) { + if (rv.length >= 4) { Q[0] = rv[3]; } else { Q[0] = 1 - rv[0]*rv[0] - rv[1]*rv[1] - rv[2]*rv[2]; diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java index 22543e3..a725bec 100644 --- a/core/java/android/net/LinkAddress.java +++ b/core/java/android/net/LinkAddress.java @@ -24,13 +24,13 @@ import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.UnknownHostException; -import static libcore.io.OsConstants.IFA_F_DADFAILED; -import static libcore.io.OsConstants.IFA_F_DEPRECATED; -import static libcore.io.OsConstants.IFA_F_TENTATIVE; -import static libcore.io.OsConstants.RT_SCOPE_HOST; -import static libcore.io.OsConstants.RT_SCOPE_LINK; -import static libcore.io.OsConstants.RT_SCOPE_SITE; -import static libcore.io.OsConstants.RT_SCOPE_UNIVERSE; +import static android.system.OsConstants.IFA_F_DADFAILED; +import static android.system.OsConstants.IFA_F_DEPRECATED; +import static android.system.OsConstants.IFA_F_TENTATIVE; +import static android.system.OsConstants.RT_SCOPE_HOST; +import static android.system.OsConstants.RT_SCOPE_LINK; +import static android.system.OsConstants.RT_SCOPE_SITE; +import static android.system.OsConstants.RT_SCOPE_UNIVERSE; /** * Identifies an IP address on a network link. diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java index 119e533..fa9f479 100644 --- a/core/java/android/net/LocalSocketImpl.java +++ b/core/java/android/net/LocalSocketImpl.java @@ -22,9 +22,9 @@ import java.io.InputStream; import java.io.FileDescriptor; import java.net.SocketOptions; -import libcore.io.ErrnoException; -import libcore.io.Libcore; -import libcore.io.OsConstants; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; /** * Socket implementation used for android.net.LocalSocket and @@ -56,7 +56,10 @@ class LocalSocketImpl /** {@inheritDoc} */ @Override public int available() throws IOException { - return available_native(fd); + FileDescriptor myFd = fd; + if (myFd == null) throw new IOException("socket closed"); + + return available_native(myFd); } /** {@inheritDoc} */ @@ -248,7 +251,7 @@ class LocalSocketImpl throw new IllegalStateException("unknown sockType"); } try { - fd = Libcore.os.socket(OsConstants.AF_UNIX, osType, 0); + fd = Os.socket(OsConstants.AF_UNIX, osType, 0); mFdCreatedInternally = true; } catch (ErrnoException e) { e.rethrowAsIOException(); @@ -268,7 +271,7 @@ class LocalSocketImpl return; } try { - Libcore.os.close(fd); + Os.close(fd); } catch (ErrnoException e) { e.rethrowAsIOException(); } diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java index 3652a4c..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 @@ import javax.net.ssl.X509TrustManager; * {@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(); + + /** + * The singleton instance of the hostname verifier. + */ + private static final HostnameVerifier sVerifier = HttpsURLConnection + .getDefaultHostnameVerifier(); + } - private static final DefaultHostnameVerifier sVerifier - = new DefaultHostnameVerifier(); + 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 @@ public class CertificateChainValidator { * Handles updates to credential storage. */ public static void handleTrustStorageUpdate() { - + TrustManagerFactory tmf; try { - X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultTrustManager(); - 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 @@ public class CertificateChainValidator { 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 @@ public class CertificateChainValidator { } try { - X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultTrustManager(); - 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 @@ public class CertificateChainValidator { } } + /** + * Returns the platform default {@link X509ExtendedTrustManager}. + */ + private X509ExtendedTrustManager getTrustManager() { + return mTrustManager; + } private void closeSocketThrowException( SSLSocket socket, String errorMessage, String defaultErrorMessage) @@ -217,4 +268,4 @@ public class CertificateChainValidator { 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.CertificateException; 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 @@ public class X509TrustManagerExtensions { 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 class X509TrustManagerExtensions { */ 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/nfc/tech/Ndef.java b/core/java/android/nfc/tech/Ndef.java index 64aa299..f16dc3b 100644 --- a/core/java/android/nfc/tech/Ndef.java +++ b/core/java/android/nfc/tech/Ndef.java @@ -278,6 +278,8 @@ public final class Ndef extends BasicTagTechnology { throw new TagLostException(); } return msg; + } else if (!tagService.isPresent(serviceHandle)) { + throw new TagLostException(); } else { return null; } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 51f32b0..65f62ed 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -74,7 +74,41 @@ public class Build { /** A hardware serial number, if available. Alphanumeric only, case-insensitive. */ public static final String SERIAL = getString("ro.serialno"); - + + /** + * An ordered list of ABIs supported by this device. The most preferred ABI is the first + * element in the list. + * + * See {@link #SUPPORTED_32_BIT_ABIS} and {@link #SUPPORTED_64_BIT_ABIS}. + * + * @hide + */ + public static final String[] SUPPORTED_ABIS = SystemProperties.get("ro.product.cpu.abilist") + .split(","); + + /** + * An ordered list of <b>32 bit</b> ABIs supported by this device. The most preferred ABI + * is the first element in the list. + * + * See {@link #SUPPORTED_ABIS} and {@link #SUPPORTED_64_BIT_ABIS}. + * + * @hide + */ + public static final String[] SUPPORTED_32_BIT_ABIS = + SystemProperties.get("ro.product.cpu.abilist32").split(","); + + /** + * An ordered list of <b>64 bit</b> ABIs supported by this device. The most preferred ABI + * is the first element in the list. + * + * See {@link #SUPPORTED_ABIS} and {@link #SUPPORTED_32_BIT_ABIS}. + * + * @hide + */ + public static final String[] SUPPORTED_64_BIT_ABIS = + SystemProperties.get("ro.product.cpu.abilist64").split(","); + + /** Various version strings. */ public static class VERSION { /** diff --git a/core/java/android/os/CommonClock.java b/core/java/android/os/CommonClock.java index 3a1da97..7f41c5d 100644 --- a/core/java/android/os/CommonClock.java +++ b/core/java/android/os/CommonClock.java @@ -20,7 +20,6 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetSocketAddress; import java.util.NoSuchElementException; -import static libcore.io.OsConstants.*; import android.content.ComponentName; import android.content.Context; @@ -32,6 +31,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; +import static android.system.OsConstants.*; /** * Used for accessing the android common time service's common clock and receiving notifications diff --git a/core/java/android/os/CommonTimeUtils.java b/core/java/android/os/CommonTimeUtils.java index 20755d9..ba060b8 100644 --- a/core/java/android/os/CommonTimeUtils.java +++ b/core/java/android/os/CommonTimeUtils.java @@ -20,7 +20,7 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetSocketAddress; import java.util.Locale; -import static libcore.io.OsConstants.*; +import static android.system.OsConstants.*; class CommonTimeUtils { /** diff --git a/core/java/android/os/CountDownTimer.java b/core/java/android/os/CountDownTimer.java index 15e6405..58acbcf 100644 --- a/core/java/android/os/CountDownTimer.java +++ b/core/java/android/os/CountDownTimer.java @@ -16,8 +16,6 @@ package android.os; -import android.util.Log; - /** * Schedule a countdown until a time in the future, with * regular notifications on intervals along the way. @@ -56,6 +54,11 @@ public abstract class CountDownTimer { private final long mCountdownInterval; private long mStopTimeInFuture; + + /** + * boolean representing if the timer was cancelled + */ + private boolean mCancelled = false; /** * @param millisInFuture The number of millis in the future from the call @@ -72,7 +75,8 @@ public abstract class CountDownTimer { /** * Cancel the countdown. */ - public final void cancel() { + public synchronized final void cancel() { + mCancelled = true; mHandler.removeMessages(MSG); } @@ -80,6 +84,7 @@ public abstract class CountDownTimer { * Start the countdown. */ public synchronized final CountDownTimer start() { + mCancelled = false; if (mMillisInFuture <= 0) { onFinish(); return this; @@ -112,6 +117,10 @@ public abstract class CountDownTimer { public void handleMessage(Message msg) { synchronized (CountDownTimer.this) { + if (mCancelled) { + return; + } + final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime(); if (millisLeft <= 0) { diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index b5413db..b554e9f 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -42,6 +42,8 @@ public class Environment { private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE"; private static final String ENV_SECONDARY_STORAGE = "SECONDARY_STORAGE"; private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT"; + private static final String ENV_OEM_ROOT = "OEM_ROOT"; + private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT"; /** {@hide} */ public static final String DIR_ANDROID = "Android"; @@ -56,6 +58,8 @@ public class Environment { public static final String DIRECTORY_ANDROID = DIR_ANDROID; private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system"); + private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem"); + private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor"); private static final File DIR_MEDIA_STORAGE = getDirectory(ENV_MEDIA_STORAGE, "/data/media"); private static final String CANONCIAL_EMULATED_STORAGE_TARGET = getCanonicalPathOrNull( @@ -244,6 +248,25 @@ public class Environment { } /** + * Return root directory of the "oem" partition holding OEM customizations, + * if any. If present, the partition is mounted read-only. + * + * @hide + */ + public static File getOemDirectory() { + return DIR_OEM_ROOT; + } + + /** + * Return root directory of the "vendor" partition that holds vendor-provided + * software that should persist across simple reflashing of the "system" partition. + * @hide + */ + public static File getVendorDirectory() { + return DIR_VENDOR_ROOT; + } + + /** * Gets the system directory available for secure storage. * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure/system). * Otherwise, it returns the unencrypted /data/system directory. @@ -296,6 +319,17 @@ public class Environment { } /** + * Returns the config directory for a user. This is for use by system services to store files + * relating to the user which should be readable by any app running as that user. + * + * @hide + */ + public static File getUserConfigDirectory(int userId) { + return new File(new File(new File( + getDataDirectory(), "misc"), "user"), Integer.toString(userId)); + } + + /** * Returns whether the Encrypted File System feature is enabled on the device or not. * @return <code>true</code> if Encrypted File System feature is enabled, <code>false</code> * if disabled. diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index ff3e277..3e0b54a 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -16,13 +16,13 @@ package android.os; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; import android.util.Log; import android.util.Slog; -import libcore.io.ErrnoException; import libcore.io.IoUtils; -import libcore.io.Libcore; -import libcore.io.OsConstants; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; @@ -87,7 +87,7 @@ public class FileUtils { */ public static int setPermissions(String path, int mode, int uid, int gid) { try { - Libcore.os.chmod(path, mode); + Os.chmod(path, mode); } catch (ErrnoException e) { Slog.w(TAG, "Failed to chmod(" + path + "): " + e); return e.errno; @@ -95,7 +95,7 @@ public class FileUtils { if (uid >= 0 || gid >= 0) { try { - Libcore.os.chown(path, uid, gid); + Os.chown(path, uid, gid); } catch (ErrnoException e) { Slog.w(TAG, "Failed to chown(" + path + "): " + e); return e.errno; @@ -115,7 +115,7 @@ public class FileUtils { */ public static int setPermissions(FileDescriptor fd, int mode, int uid, int gid) { try { - Libcore.os.fchmod(fd, mode); + Os.fchmod(fd, mode); } catch (ErrnoException e) { Slog.w(TAG, "Failed to fchmod(): " + e); return e.errno; @@ -123,7 +123,7 @@ public class FileUtils { if (uid >= 0 || gid >= 0) { try { - Libcore.os.fchown(fd, uid, gid); + Os.fchown(fd, uid, gid); } catch (ErrnoException e) { Slog.w(TAG, "Failed to fchown(): " + e); return e.errno; @@ -138,7 +138,7 @@ public class FileUtils { */ public static int getUid(String path) { try { - return Libcore.os.stat(path).st_uid; + return Os.stat(path).st_uid; } catch (ErrnoException e) { return -1; } @@ -357,4 +357,26 @@ public class FileUtils { } } } + + /** + * Test if a file lives under the given directory, either as a direct child + * or a distant grandchild. + * <p> + * Both files <em>must</em> have been resolved using + * {@link File#getCanonicalFile()} to avoid symlink or path traversal + * attacks. + */ + public static boolean contains(File dir, File file) { + String dirPath = dir.getAbsolutePath(); + String filePath = file.getAbsolutePath(); + + if (dirPath.equals(filePath)) { + return true; + } + + if (!dirPath.endsWith("/")) { + dirPath += "/"; + } + return filePath.startsWith(dirPath); + } } 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 @@ public class MemoryFile * 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/Parcel.java b/core/java/android/os/Parcel.java index 6716098..7425f67 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -1246,9 +1246,6 @@ public final class Parcel { } else if (v instanceof Parcelable[]) { writeInt(VAL_PARCELABLEARRAY); writeParcelableArray((Parcelable[]) v, 0); - } else if (v instanceof Object[]) { - writeInt(VAL_OBJECTARRAY); - writeArray((Object[]) v); } else if (v instanceof int[]) { writeInt(VAL_INTARRAY); writeIntArray((int[]) v); @@ -1258,12 +1255,20 @@ public final class Parcel { } else if (v instanceof Byte) { writeInt(VAL_BYTE); writeInt((Byte) v); - } else if (v instanceof Serializable) { - // Must be last - writeInt(VAL_SERIALIZABLE); - writeSerializable((Serializable) v); } else { - throw new RuntimeException("Parcel: unable to marshal value " + v); + Class<?> clazz = v.getClass(); + if (clazz.isArray() && clazz.getComponentType() == Object.class) { + // Only pure Object[] are written here, Other arrays of non-primitive types are + // handled by serialization as this does not record the component type. + writeInt(VAL_OBJECTARRAY); + writeArray((Object[]) v); + } else if (v instanceof Serializable) { + // Must be last + writeInt(VAL_SERIALIZABLE); + writeSerializable((Serializable) v); + } else { + throw new RuntimeException("Parcel: unable to marshal value " + v); + } } } @@ -1454,10 +1459,11 @@ public final class Parcel { } /** - * Use this function for customized exception handling. - * customized method call this method for all unknown case - * @param code exception code - * @param msg exception message + * Throw an exception with the given message. Not intended for use + * outside the Parcel class. + * + * @param code Used to determine which exception class to throw. + * @param msg The exception message. */ public final void readException(int code, String msg) { switch (code) { diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 5273c20..59795da 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -16,24 +16,24 @@ package android.os; -import static libcore.io.OsConstants.AF_UNIX; -import static libcore.io.OsConstants.SEEK_SET; -import static libcore.io.OsConstants.SOCK_STREAM; -import static libcore.io.OsConstants.S_ISLNK; -import static libcore.io.OsConstants.S_ISREG; +import static android.system.OsConstants.AF_UNIX; +import static android.system.OsConstants.SEEK_SET; +import static android.system.OsConstants.SOCK_STREAM; +import static android.system.OsConstants.S_ISLNK; +import static android.system.OsConstants.S_ISREG; import android.content.BroadcastReceiver; import android.content.ContentProvider; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; +import android.system.StructStat; import android.util.Log; import dalvik.system.CloseGuard; -import libcore.io.ErrnoException; import libcore.io.IoUtils; -import libcore.io.Libcore; import libcore.io.Memory; -import libcore.io.OsConstants; -import libcore.io.StructStat; import java.io.Closeable; import java.io.File; @@ -42,6 +42,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InterruptedIOException; import java.net.DatagramSocket; import java.net.Socket; import java.nio.ByteOrder; @@ -260,7 +261,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { */ public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException { try { - final FileDescriptor fd = Libcore.os.dup(orig); + final FileDescriptor fd = Os.dup(orig); return new ParcelFileDescriptor(fd); } catch (ErrnoException e) { throw e.rethrowAsIOException(); @@ -296,7 +297,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { original.setInt$(fd); try { - final FileDescriptor dup = Libcore.os.dup(original); + final FileDescriptor dup = Os.dup(original); return new ParcelFileDescriptor(dup); } catch (ErrnoException e) { throw e.rethrowAsIOException(); @@ -358,7 +359,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { */ public static ParcelFileDescriptor[] createPipe() throws IOException { try { - final FileDescriptor[] fds = Libcore.os.pipe(); + final FileDescriptor[] fds = Os.pipe(); return new ParcelFileDescriptor[] { new ParcelFileDescriptor(fds[0]), new ParcelFileDescriptor(fds[1]) }; @@ -380,7 +381,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { public static ParcelFileDescriptor[] createReliablePipe() throws IOException { try { final FileDescriptor[] comm = createCommSocketPair(); - final FileDescriptor[] fds = Libcore.os.pipe(); + final FileDescriptor[] fds = Os.pipe(); return new ParcelFileDescriptor[] { new ParcelFileDescriptor(fds[0], comm[0]), new ParcelFileDescriptor(fds[1], comm[1]) }; @@ -397,7 +398,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { try { final FileDescriptor fd0 = new FileDescriptor(); final FileDescriptor fd1 = new FileDescriptor(); - Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1); + Os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1); return new ParcelFileDescriptor[] { new ParcelFileDescriptor(fd0), new ParcelFileDescriptor(fd1) }; @@ -420,7 +421,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { final FileDescriptor[] comm = createCommSocketPair(); final FileDescriptor fd0 = new FileDescriptor(); final FileDescriptor fd1 = new FileDescriptor(); - Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1); + Os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1); return new ParcelFileDescriptor[] { new ParcelFileDescriptor(fd0, comm[0]), new ParcelFileDescriptor(fd1, comm[1]) }; @@ -433,7 +434,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { try { final FileDescriptor comm1 = new FileDescriptor(); final FileDescriptor comm2 = new FileDescriptor(); - Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, comm1, comm2); + Os.socketpair(AF_UNIX, SOCK_STREAM, 0, comm1, comm2); IoUtils.setBlocking(comm1, false); IoUtils.setBlocking(comm2, false); return new FileDescriptor[] { comm1, comm2 }; @@ -519,7 +520,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { return mWrapped.getStatSize(); } else { try { - final StructStat st = Libcore.os.fstat(mFd); + final StructStat st = Os.fstat(mFd); if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { return st.st_size; } else { @@ -542,7 +543,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { return mWrapped.seekTo(pos); } else { try { - return Libcore.os.lseek(mFd, pos, SEEK_SET); + return Os.lseek(mFd, pos, SEEK_SET); } catch (ErrnoException e) { throw e.rethrowAsIOException(); } @@ -694,10 +695,13 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { writePtr += len; } - Libcore.os.write(mCommFd, buf, 0, writePtr); + Os.write(mCommFd, buf, 0, writePtr); } catch (ErrnoException e) { // Reporting status is best-effort Log.w(TAG, "Failed to report status: " + e); + } catch (InterruptedIOException e) { + // Reporting status is best-effort + Log.w(TAG, "Failed to report status: " + e); } } finally { @@ -708,7 +712,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { private static Status readCommStatus(FileDescriptor comm, byte[] buf) { try { - final int n = Libcore.os.read(comm, buf, 0, buf.length); + final int n = Os.read(comm, buf, 0, buf.length); if (n == 0) { // EOF means they're dead return new Status(Status.DEAD); @@ -728,6 +732,9 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { Log.d(TAG, "Failed to read status; assuming dead: " + e); return new Status(Status.DEAD); } + } catch (InterruptedIOException e) { + Log.d(TAG, "Failed to read status; assuming dead: " + e); + return new Status(Status.DEAD); } } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 057f516..86c749a 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -16,27 +16,32 @@ package android.os; -import android.net.LocalSocketAddress; import android.net.LocalSocket; +import android.net.LocalSocketAddress; +import android.system.Os; 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 libcore.io.Libcore; +import java.util.Arrays; +import java.util.List; /*package*/ class ZygoteStartFailedEx extends Exception { - /** - * Something prevented the zygote process startup from happening normally - */ + ZygoteStartFailedEx(String s) { + super(s); + } - ZygoteStartFailedEx() {}; - ZygoteStartFailedEx(String s) {super(s);} - ZygoteStartFailedEx(Throwable cause) {super(cause);} + ZygoteStartFailedEx(Throwable cause) { + super(cause); + } + + ZygoteStartFailedEx(String s, Throwable cause) { + super(s, cause); + } } /** @@ -45,19 +50,15 @@ import libcore.io.Libcore; public class Process { private static final String LOG_TAG = "Process"; - private static final String ZYGOTE_SOCKET = "zygote"; - /** - * Name of a process for running the platform's media services. - * {@hide} + * @hide for internal use only. */ - public static final String ANDROID_SHARED_MEDIA = "com.android.process.media"; + public static final String ZYGOTE_SOCKET = "zygote"; /** - * Name of the process that Google content providers can share. - * {@hide} + * @hide for internal use only. */ - public static final String GOOGLE_SHARED_APP_CONTENT = "com.google.process.content"; + public static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary"; /** * Defines the UID/GID under which system code runs. @@ -155,6 +156,12 @@ public class Process { public static final int LAST_ISOLATED_UID = 99999; /** + * Defines the gid shared by all applications running under the same profile. + * @hide + */ + public static final int SHARED_USER_GID = 9997; + + /** * First gid for applications to share resources. Used when forward-locking * is enabled but all UserHandles need to be able to read the resources. * @hide @@ -344,15 +351,85 @@ public class Process { 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. + * + * @hide for internal use only. + */ + public static class ZygoteState { + final LocalSocket socket; + final DataInputStream inputStream; + final BufferedWriter writer; + final List<String> abiList; + + 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; + } + + public static ZygoteState connect(String socketAddress) throws IOException { + DataInputStream zygoteInputStream = null; + BufferedWriter zygoteWriter = null; + final LocalSocket zygoteSocket = new LocalSocket(); + + try { + zygoteSocket.connect(new LocalSocketAddress(socketAddress, + LocalSocketAddress.Namespace.RESERVED)); - /** true if previous zygote open failed */ - static boolean sPreviousZygoteOpenFailed; + zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream()); + + zygoteWriter = new BufferedWriter(new OutputStreamWriter( + zygoteSocket.getOutputStream()), 256); + } catch (IOException ex) { + try { + zygoteSocket.close(); + } catch (IOException ignore) { + } + + throw ex; + } + + 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); + } + + public 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 +455,7 @@ public class Process { * @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 +469,12 @@ public class Process { 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,93 +487,39 @@ public class Process { 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. - */ - private static void openZygoteSocketIfNeeded() - throws ZygoteStartFailedEx { - - int retryCount; - - if (sPreviousZygoteOpenFailed) { - /* - * If we've failed before, expect that we'll fail again and - * don't pause for retries. - */ - retryCount = 0; - } else { - retryCount = 10; - } - - /* - * 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"); - } + * Queries the zygote for the list of ABIS it supports. + * + * @throws ZygoteStartFailedEx if the query failed. + */ + private static String getAbiList(BufferedWriter writer, DataInputStream inputStream) + throws IOException { + // 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(); + + // The response is a length prefixed stream of ASCII bytes. + int numBytes = inputStream.readInt(); + byte[] bytes = new byte[numBytes]; + inputStream.readFully(bytes); + + return new String(bytes, StandardCharsets.US_ASCII); } /** * 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 +531,11 @@ public class Process { * 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 +544,22 @@ public class Process { 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 +576,7 @@ public class Process { * @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 +588,7 @@ public class Process { int debugFlags, int mountExternal, int targetSdkVersion, String seInfo, + String abi, String[] extraArgs) throws ZygoteStartFailedEx { synchronized(Process.class) { @@ -636,10 +656,43 @@ public class Process { } } - return zygoteSendArgsAndGetResult(argsForZygote); + return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote); } } - + + /** + * 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()) { + try { + primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET); + } catch (IOException ioe) { + throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe); + } + } + + if (primaryZygoteState.matches(abi)) { + return primaryZygoteState; + } + + // The primary zygote didn't match. Try the secondary. + if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) { + try { + secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET); + } catch (IOException ioe) { + throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe); + } + } + + 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. @@ -651,7 +704,7 @@ public class Process { * {@link #killProcess} and {@link #sendSignal}. */ public static final int myPid() { - return Libcore.os.getpid(); + return Os.getpid(); } /** @@ -659,7 +712,7 @@ public class Process { * @hide */ public static final int myPpid() { - return Libcore.os.getppid(); + return Os.getppid(); } /** @@ -667,7 +720,7 @@ public class Process { * {@link #setThreadPriority(int, int)}. */ public static final int myTid() { - return Libcore.os.gettid(); + return Os.gettid(); } /** @@ -677,7 +730,7 @@ public class Process { * a uid identifies a specific app sandbox in a specific user. */ public static final int myUid() { - return Libcore.os.getuid(); + return Os.getuid(); } /** diff --git a/core/java/android/os/StatFs.java b/core/java/android/os/StatFs.java index 9e9521a..13e9a15 100644 --- a/core/java/android/os/StatFs.java +++ b/core/java/android/os/StatFs.java @@ -16,9 +16,9 @@ package android.os; -import libcore.io.ErrnoException; -import libcore.io.Libcore; -import libcore.io.StructStatVfs; +import android.system.ErrnoException; +import android.system.Os; +import android.system.StructStatVfs; /** * Retrieve overall information about the space on a filesystem. This is a @@ -41,7 +41,7 @@ public class StatFs { private static StructStatVfs doStat(String path) { try { - return Libcore.os.statvfs(path); + return Os.statvfs(path); } catch (ErrnoException e) { throw new IllegalArgumentException("Invalid path: " + path, e); } diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java index 729c64b..672df6d 100644 --- a/core/java/android/os/SystemClock.java +++ b/core/java/android/os/SystemClock.java @@ -16,6 +16,9 @@ package android.os; +import android.app.IAlarmManager; +import android.content.Context; +import android.util.Slog; /** * Core timekeeping facilities. @@ -89,6 +92,8 @@ package android.os; * </ul> */ public final class SystemClock { + private static final String TAG = "SystemClock"; + /** * This class is uninstantiable. */ @@ -134,7 +139,23 @@ public final class SystemClock { * * @return if the clock was successfully set to the specified time. */ - native public static boolean setCurrentTimeMillis(long millis); + public static boolean setCurrentTimeMillis(long millis) { + IBinder b = ServiceManager.getService(Context.ALARM_SERVICE); + IAlarmManager mgr = IAlarmManager.Stub.asInterface(b); + if (mgr == null) { + return false; + } + + try { + return mgr.setTime(millis); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to set RTC", e); + } catch (SecurityException e) { + Slog.e(TAG, "Unable to set RTC", e); + } + + return false; + } /** * Returns milliseconds since boot, not counting time spent in deep sleep. diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index 3249bcb..bc84e64 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -72,6 +72,8 @@ public final class Trace { public static final long TRACE_TAG_DALVIK = 1L << 14; /** @hide */ public static final long TRACE_TAG_RS = 1L << 15; + /** @hide */ + public static final long TRACE_TAG_BIONIC = 1L << 16; private static final long TRACE_TAG_NOT_READY = 1L << 63; private static final int MAX_SECTION_NAME_LEN = 127; diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java index 6e693a4..914c170 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -145,6 +145,14 @@ public final class UserHandle implements Parcelable { } /** + * Returns the gid shared between all apps with this userId. + * @hide + */ + public static final int getUserGid(int userId) { + return getUid(userId, Process.SHARED_USER_GID); + } + + /** * Returns the shared app gid for a given uid or appId. * @hide */ diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index f0520b5..6519f7e 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -17,7 +17,7 @@ package android.provider; import static android.net.TrafficStats.KB_IN_BYTES; -import static libcore.io.OsConstants.SEEK_SET; +import static android.system.OsConstants.SEEK_SET; import android.content.ContentProviderClient; import android.content.ContentResolver; @@ -38,11 +38,11 @@ import android.os.OperationCanceledException; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor.OnCloseListener; import android.os.RemoteException; +import android.system.ErrnoException; +import android.system.Os; import android.util.Log; -import libcore.io.ErrnoException; import libcore.io.IoUtils; -import libcore.io.Libcore; import java.io.BufferedInputStream; import java.io.File; @@ -697,7 +697,7 @@ public final class DocumentsContract { // optimal decode path; otherwise fall back to buffering. BufferedInputStream is = null; try { - Libcore.os.lseek(fd, offset, SEEK_SET); + Os.lseek(fd, offset, SEEK_SET); } catch (ErrnoException e) { is = new BufferedInputStream(new FileInputStream(fd), THUMBNAIL_BUFFER_SIZE); is.mark(THUMBNAIL_BUFFER_SIZE); @@ -723,7 +723,7 @@ public final class DocumentsContract { bitmap = BitmapFactory.decodeStream(is, null, opts); } else { try { - Libcore.os.lseek(fd, offset, SEEK_SET); + Os.lseek(fd, offset, SEEK_SET); } catch (ErrnoException e) { e.rethrowAsIOException(); } diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java index 0bd46bc..b17f502 100644 --- a/core/java/android/text/method/QwertyKeyListener.java +++ b/core/java/android/text/method/QwertyKeyListener.java @@ -115,7 +115,7 @@ public class QwertyKeyListener extends BaseKeyListener { if (count > 0 && selStart == selEnd && selStart > 0) { char c = content.charAt(selStart - 1); - if (c == i || c == Character.toUpperCase(i) && view != null) { + if ((c == i || c == Character.toUpperCase(i)) && view != null) { if (showCharacterPicker(view, content, c, false, count)) { resetMetaState(content); return true; diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java index 29c0ba2..aefced8 100644 --- a/core/java/android/util/EventLog.java +++ b/core/java/android/util/EventLog.java @@ -56,16 +56,18 @@ public class EventLog { public static final class Event { private final ByteBuffer mBuffer; - // Layout of event log entry received from kernel. + // Layout of event log entry received from Android logger. + // see system/core/include/log/logger.h private static final int LENGTH_OFFSET = 0; + private static final int HEADER_SIZE_OFFSET = 2; private static final int PROCESS_OFFSET = 4; private static final int THREAD_OFFSET = 8; private static final int SECONDS_OFFSET = 12; private static final int NANOSECONDS_OFFSET = 16; - private static final int PAYLOAD_START = 20; - private static final int TAG_OFFSET = 20; - private static final int DATA_START = 24; + // Layout for event log v1 format, v2 and v3 use HEADER_SIZE_OFFSET + private static final int V1_PAYLOAD_START = 20; + private static final int DATA_OFFSET = 4; // Value types private static final byte INT_TYPE = 0; @@ -97,14 +99,22 @@ public class EventLog { /** @return the type tag code of the entry */ public int getTag() { - return mBuffer.getInt(TAG_OFFSET); + int offset = mBuffer.getShort(HEADER_SIZE_OFFSET); + if (offset == 0) { + offset = V1_PAYLOAD_START; + } + return mBuffer.getInt(offset); } /** @return one of Integer, Long, String, null, or Object[] of same. */ public synchronized Object getData() { try { - mBuffer.limit(PAYLOAD_START + mBuffer.getShort(LENGTH_OFFSET)); - mBuffer.position(DATA_START); // Just after the tag. + int offset = mBuffer.getShort(HEADER_SIZE_OFFSET); + if (offset == 0) { + offset = V1_PAYLOAD_START; + } + mBuffer.limit(offset + mBuffer.getShort(LENGTH_OFFSET)); + mBuffer.position(offset + DATA_OFFSET); // Just after the tag. return decodeObject(); } catch (IllegalArgumentException e) { Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e); diff --git a/core/java/android/util/JsonReader.java b/core/java/android/util/JsonReader.java index f2a86c9..7d1c6c4 100644 --- a/core/java/android/util/JsonReader.java +++ b/core/java/android/util/JsonReader.java @@ -546,6 +546,9 @@ public final class JsonReader implements Closeable { public void skipValue() throws IOException { skipping = true; try { + if (!hasNext() || peek() == JsonToken.END_DOCUMENT) { + throw new IllegalStateException("No element left to skip"); + } int count = 0; do { JsonToken token = advance(); diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java index abd173a..2b81072 100644 --- a/core/java/android/util/Log.java +++ b/core/java/android/util/Log.java @@ -352,6 +352,7 @@ public final class Log { /** @hide */ public static final int LOG_ID_RADIO = 1; /** @hide */ public static final int LOG_ID_EVENTS = 2; /** @hide */ public static final int LOG_ID_SYSTEM = 3; + /** @hide */ public static final int LOG_ID_CRASH = 4; /** @hide */ public static native int println_native(int bufID, int priority, String tag, String msg); diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java index 9522112..0f8da44 100644 --- a/core/java/android/util/Patterns.java +++ b/core/java/android/util/Patterns.java @@ -191,8 +191,6 @@ public class Patterns { for (int i = 1; i <= numGroups; i++) { String s = matcher.group(i); - System.err.println("Group(" + i + ") : " + s); - if (s != null) { b.append(s); } diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 6a15fa6..d533060 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -392,11 +392,11 @@ class GLES20Canvas extends HardwareCanvas { // Atlas /////////////////////////////////////////////////////////////////////////// - static void initAtlas(GraphicBuffer buffer, int[] map) { + static void initAtlas(GraphicBuffer buffer, long[] map) { nInitAtlas(buffer, map, map.length); } - private static native void nInitAtlas(GraphicBuffer buffer, int[] map, int count); + private static native void nInitAtlas(GraphicBuffer buffer, long[] map, int count); /////////////////////////////////////////////////////////////////////////// // Display list diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 3781bdb..9097a6c 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -1555,10 +1555,6 @@ public abstract class HardwareRenderer { } private DisplayList buildDisplayList(View view, HardwareCanvas canvas) { - if (mDrawDelta <= 0) { - return view.mDisplayList; - } - view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) == View.PFLAG_INVALIDATED; view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; @@ -1981,7 +1977,7 @@ public abstract class HardwareRenderer { if (atlas.isCompatible(android.os.Process.myPpid())) { GraphicBuffer buffer = atlas.getBuffer(); if (buffer != null) { - int[] map = atlas.getMap(); + long[] map = atlas.getMap(); if (map != null) { GLES20Canvas.initAtlas(buffer, map); } diff --git a/core/java/android/view/IAssetAtlas.aidl b/core/java/android/view/IAssetAtlas.aidl index 5f1e238..edce059 100644 --- a/core/java/android/view/IAssetAtlas.aidl +++ b/core/java/android/view/IAssetAtlas.aidl @@ -45,10 +45,10 @@ interface IAssetAtlas { * if the atlas is not available yet. * * Each bitmap is represented by several entries in the array: - * int0: SkBitmap*, the native bitmap object - * int1: x position - * int2: y position - * int3: rotated, 1 if the bitmap must be rotated, 0 otherwise + * long0: SkBitmap*, the native bitmap object + * long1: x position + * long2: y position + * long3: rotated, 1 if the bitmap must be rotated, 0 otherwise */ - int[] getMap(); + long[] getMap(); } 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 @@ public abstract class LayoutInflater { 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/SurfaceSession.java b/core/java/android/view/SurfaceSession.java index 0dfd94a..3cf5af4 100644 --- a/core/java/android/view/SurfaceSession.java +++ b/core/java/android/view/SurfaceSession.java @@ -24,11 +24,11 @@ package android.view; */ public final class SurfaceSession { // Note: This field is accessed by native code. - private int mNativeClient; // SurfaceComposerClient* + private long mNativeClient; // SurfaceComposerClient* - private static native int nativeCreate(); - private static native void nativeDestroy(int ptr); - private static native void nativeKill(int ptr); + private static native long nativeCreate(); + private static native void nativeDestroy(long ptr); + private static native void nativeKill(long ptr); /** Create a new connection with the surface flinger. */ public SurfaceSession() { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 4c53df7..872cbe7 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -10239,7 +10239,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * by the layout system and should not generally be called otherwise, because the property * may be changed at any time by the layout. * - * @param left The bottom of this view, in pixels. + * @param left The left of this view, in pixels. */ public final void setLeft(int left) { if (left != mLeft) { @@ -10306,7 +10306,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * by the layout system and should not generally be called otherwise, because the property * may be changed at any time by the layout. * - * @param right The bottom of this view, in pixels. + * @param right The right of this view, in pixels. */ public final void setRight(int right) { if (right != mRight) { @@ -11983,7 +11983,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * <p>Compute the vertical extent of the horizontal scrollbar's thumb + * <p>Compute the vertical extent of the vertical scrollbar's thumb * within the vertical range. This value is used to compute the length * of the thumb within the scrollbar's track.</p> * diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7d5881c..1cb0473 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -661,7 +661,7 @@ public final class ViewRootImpl implements ViewParent, 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); @@ -669,7 +669,7 @@ public final class ViewRootImpl implements ViewParent, 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/webkit/ClientCertRequest.java b/core/java/android/webkit/ClientCertRequest.java new file mode 100644 index 0000000..8951786 --- /dev/null +++ b/core/java/android/webkit/ClientCertRequest.java @@ -0,0 +1,80 @@ +/* + * 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 android.webkit; + +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +/** + * ClientCertRequest: The user receives an instance of this class as + * a parameter of {@link WebViewClient#onReceivedClientCertRequest}. + * The request includes the parameters to choose the client certificate, + * such as the host name and the port number requesting the cert, the acceptable + * key types and the principals. + * + * The user should call one of the interface methods to indicate how to deal + * with the client certificate request. All methods should be called on + * UI thread. + * + * WebView caches the {@link #proceed} and {@link #cancel} responses in memory + * and uses them to handle future client certificate requests for the same + * host/port pair. The user can clear the cached data using + * {@link WebView#clearClientCertPreferences}. + * + * TODO(sgurun) unhide + * @hide + */ +public interface ClientCertRequest { + /** + * Returns the acceptable types of asymmetric keys (can be null). + */ + public String[] getKeyTypes(); + + /** + * Returns the acceptable certificate issuers for the certificate + * matching the private key (can be null). + */ + public Principal[] getPrincipals(); + + /** + * Returns the host name of the server requesting the certificate. + */ + public String getHost(); + + /** + * Returns the port number of the server requesting the certificate. + */ + public int getPort(); + + /** + * Proceed with the specified private key and client certificate chain. + * Remember the user's positive choice and use it for future requests. + */ + public void proceed(PrivateKey privateKey, X509Certificate[] chain); + + /** + * Ignore the request for now. Do not remember user's choice. + */ + public void ignore(); + + /** + * Cancel this request. Remember the user's choice and use it for + * future requests. + */ + public void cancel(); +} diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index e8974c6..fb842ff 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -204,6 +204,30 @@ public class WebViewClient { handler.cancel(); } + /** + * Notify the host application to handle a SSL client certificate + * request. The host application is responsible for showing the UI + * if desired and providing the keys. There are three ways to + * respond: proceed(), cancel() or ignore(). Webview remembers the + * response if proceed() or cancel() is called and does not + * call onReceivedClientCertRequest() again for the same host and port + * pair. Webview does not remember the response if ignore() is called. + * + * This method is called on the UI thread. During the callback, the + * connection is suspended. + * + * The default behavior is to cancel, returning no client certificate. + * + * @param view The WebView that is initiating the callback + * @param request An instance of a {@link ClientCertRequest} + * + * TODO(sgurun) unhide + * @hide + */ + public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) { + request.cancel(); + } + /** * Notifies the host application that the WebView received an HTTP * authentication request. The host application can use the supplied diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index 7674837..fe2fc96 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -292,7 +292,7 @@ public abstract class AbsSeekBar extends ProgressBar { // The extra space for the thumb to move on the track available += mThumbOffset * 2; - int thumbPos = (int) (scale * available); + int thumbPos = (int) (scale * available + 0.5f); int topBound, bottomBound; if (gap == Integer.MIN_VALUE) { diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index a5fad60..a06344f 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -663,7 +663,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { /** * When the current adapter is empty, the AdapterView can display a special view - * call the empty view. The empty view is used to provide feedback to the user + * called the empty view. The empty view is used to provide feedback to the user * that no data is available in this AdapterView. * * @return The view to show if the adapter is empty. diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java index d03161e..ab16360 100644 --- a/core/java/android/widget/DatePicker.java +++ b/core/java/android/widget/DatePicker.java @@ -527,7 +527,7 @@ public class DatePicker extends FrameLayout { mSpinners.removeAllViews(); // We use numeric spinners for year and day, but textual months. Ask icu4c what // order the user's locale uses for that combination. http://b/7207103. - String pattern = ICU.getBestDateTimePattern("yyyyMMMdd", Locale.getDefault().toString()); + String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyyMMMdd"); char[] order = ICU.getDateFormatOrder(pattern); final int spinnerCount = order.length; for (int i = 0; i < spinnerCount; i++) { diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 9c6a2e3..2c44703 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -1965,7 +1965,16 @@ public class NumberPicker extends LinearLayout { , '\u0669', // Extended Arabic-Indic '\u06f0', '\u06f1', '\u06f2', '\u06f3', '\u06f4', '\u06f5', '\u06f6', '\u06f7', '\u06f8' - , '\u06f9' + , '\u06f9', + // Hindi and Marathi (Devanagari script) + '\u0966', '\u0967', '\u0968', '\u0969', '\u096a', '\u096b', '\u096c', '\u096d', '\u096e' + , '\u096f', + // Bengali + '\u09e6', '\u09e7', '\u09e8', '\u09e9', '\u09ea', '\u09eb', '\u09ec', '\u09ed', '\u09ee' + , '\u09ef', + // Kannada + '\u0ce6', '\u0ce7', '\u0ce8', '\u0ce9', '\u0cea', '\u0ceb', '\u0cec', '\u0ced', '\u0cee' + , '\u0cef' }; /** diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 9bfb00c..8460375 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -5147,12 +5147,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } 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 @@ public class ChooserActivity extends ResolverActivity { 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 @@ public class ChooserActivity extends ResolverActivity { 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/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl index 03d3b22..77f0dec 100644 --- a/core/java/com/android/internal/app/IMediaContainerService.aidl +++ b/core/java/com/android/internal/app/IMediaContainerService.aidl @@ -25,16 +25,18 @@ import android.content.res.ObbInfo; interface IMediaContainerService { String copyResourceToContainer(in Uri packageURI, String containerId, String key, String resFileName, String publicResFileName, boolean isExternal, - boolean isForwardLocked); + boolean isForwardLocked, in String abiOverride); int copyResource(in Uri packageURI, in ContainerEncryptionParams encryptionParams, in ParcelFileDescriptor outStream); - PackageInfoLite getMinimalPackageInfo(in String packagePath, in int flags, in long threshold); + PackageInfoLite getMinimalPackageInfo(in String packagePath, in int flags, in long threshold, + in String abiOverride); boolean checkInternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in long threshold); - boolean checkExternalFreeStorage(in Uri fileUri, boolean isForwardLocked); + boolean checkExternalFreeStorage(in Uri fileUri, boolean isForwardLocked, in String abiOverride); ObbInfo getObbInfo(in String filename); long calculateDirectorySize(in String directory); /** Return file system stats: [0] is total bytes, [1] is available bytes */ long[] getFileSystemStats(in String path); void clearDirectory(in String directory); - long calculateInstalledSize(in String packagePath, boolean isForwardLocked); + long calculateInstalledSize(in String packagePath, boolean isForwardLocked, + in String abiOverride); } diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java index 0cad33c..cd90cdb 100644 --- a/core/java/com/android/internal/app/ProcessStats.java +++ b/core/java/com/android/internal/app/ProcessStats.java @@ -1046,7 +1046,7 @@ public final class ProcessStats implements Parcelable { public boolean evaluateSystemProperties(boolean update) { boolean changed = false; - String runtime = SystemProperties.get("persist.sys.dalvik.vm.lib", + String runtime = SystemProperties.get("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary()); if (!Objects.equals(runtime, mRuntime)) { changed = true; diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 591267e..183dd05 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -484,8 +484,7 @@ public class ResolverActivity extends AlertActivity implements AdapterView.OnIte mList.clear(); if (mBaseResolveList != null) { - currentResolveList = mBaseResolveList; - mOrigResolveList = null; + currentResolveList = mOrigResolveList = mBaseResolveList; } else { currentResolveList = mOrigResolveList = mPm.queryIntentActivities( mIntent, PackageManager.MATCH_DEFAULT_ONLY diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java index 6d65782..dab3aff 100644 --- a/core/java/com/android/internal/content/NativeLibraryHelper.java +++ b/core/java/com/android/internal/content/NativeLibraryHelper.java @@ -16,10 +16,11 @@ package com.android.internal.content; -import android.os.Build; +import android.content.pm.PackageManager; import android.util.Slog; import java.io.File; +import java.io.IOException; /** * Native libraries helper. @@ -31,38 +32,76 @@ public class NativeLibraryHelper { private static final boolean DEBUG_NATIVE = false; - private static native long nativeSumNativeBinaries(String file, String cpuAbi, String cpuAbi2); + /** + * 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. + * + * @hide + */ + 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 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. + * Sums the size of native binaries in an APK for a given ABI. * - * @param apkFile APK file to scan for native libraries * @return size of all native binary files in bytes */ - 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 long sumNativeBinariesLI(ApkHandle handle, String abi) { + return nativeSumNativeBinaries(handle.apkHandle, abi); } - private native static int nativeCopyNativeBinaries(String filePath, String sharedLibraryPath, - String cpuAbi, String cpuAbi2); + 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)); @@ -103,4 +142,18 @@ public class NativeLibraryHelper { return deletedFiles; } + + // We don't care about the other return values for now. + private static final int BITCODE_PRESENT = 1; + + public static boolean hasRenderscriptBitcode(ApkHandle handle) throws IOException { + final int returnVal = hasRenderscriptBitcode(handle.apkHandle); + if (returnVal < 0) { + throw new IOException("Error scanning APK, code: " + returnVal); + } + + return (returnVal == BITCODE_PRESENT); + } + + private static native int hasRenderscriptBitcode(long apkHandle); } diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 5538dca..4a26b4b 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -55,6 +55,11 @@ public class RuntimeInit { private static final native void nativeFinishInit(); private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup); + private static int Clog_e(String tag, String msg, Throwable tr) { + return Log.println_native(Log.LOG_ID_CRASH, Log.ERROR, tag, + msg + '\n' + Log.getStackTraceString(tr)); + } + /** * Use this to log a message when a thread exits due to an uncaught * exception. The framework catches these for the main threads, so @@ -68,7 +73,7 @@ public class RuntimeInit { mCrashing = true; if (mApplicationObject == null) { - Slog.e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e); + Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e); } else { StringBuilder message = new StringBuilder(); message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n"); @@ -77,7 +82,7 @@ public class RuntimeInit { message.append("Process: ").append(processName).append(", "); } message.append("PID: ").append(Process.myPid()); - Slog.e(TAG, message.toString(), e); + Clog_e(TAG, message.toString(), e); } // Bring up crash dialog, wait for it to be dismissed @@ -85,9 +90,9 @@ public class RuntimeInit { mApplicationObject, new ApplicationErrorReport.CrashInfo(e)); } catch (Throwable t2) { try { - Slog.e(TAG, "Error reporting crash", t2); + Clog_e(TAG, "Error reporting crash", t2); } catch (Throwable t3) { - // Even Slog.e() fails! Oh well. + // Even Clog_e() fails! Oh well. } } finally { // Try everything to make sure this process goes away. 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.FileOutputStream; 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 @@ public class WrapperInit { * @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..54c532a --- /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 android.system.ErrnoException; +import android.system.Os; + +/** @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 { + 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 4f3b5b3..0c48368 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -21,11 +21,10 @@ import android.net.LocalSocket; import android.os.Process; import android.os.SELinux; import android.os.SystemProperties; +import android.system.ErrnoException; +import android.system.Os; 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,11 +34,9 @@ import java.io.FileOutputStream; 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; /** * A connection that can make spawn requests. @@ -60,7 +57,7 @@ class ZygoteConnection { 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 @@ class ZygoteConnection { 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 @@ class ZygoteConnection { } /** - * 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 @@ class ZygoteConnection { 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) + @@ -218,15 +186,43 @@ class ZygoteConnection { } if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) { - FileDescriptor[] pipeFds = Libcore.os.pipe(); + FileDescriptor[] pipeFds = Os.pipe(); childPipeFd = pipeFds[1]; serverPipeFd = pipeFds[0]; ZygoteInit.setCloseOnExec(serverPipeFd, true); } + /** + * In order to avoid leaking descriptors to the Zygote child, + * the native code must close the two Zygote socket descriptors + * in the child process before it switches from Zygote-root to + * the UID and privileges of the application being launched. + * + * In order to avoid "bad file descriptor" errors when the + * two LocalSocket objects are closed, the Posix file + * descriptors are released via a dup2() call which closes + * the socket and substitutes an open descriptor to /dev/null. + */ + + int [] fdsToClose = { -1, -1 }; + + FileDescriptor fd = mSocket.getFileDescriptor(); + + if (fd != null) { + fdsToClose[0] = fd.getInt$(); + } + + fd = ZygoteInit.getServerSocketFileDescriptor(); + + if (fd != null) { + fdsToClose[1] = fd.getInt$(); + } + + fd = null; + pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, - parsedArgs.niceName); + parsedArgs.niceName, fdsToClose); } catch (IOException ex) { logAndPrintError(newStderr, "Exception creating pipe", ex); } catch (ErrnoException ex) { @@ -260,6 +256,18 @@ class ZygoteConnection { } } + 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. */ @@ -361,6 +369,11 @@ class ZygoteConnection { 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 @@ -513,6 +526,8 @@ class ZygoteConnection { 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; } @@ -779,7 +794,7 @@ class ZygoteConnection { /** * 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) { @@ -814,6 +829,12 @@ class ZygoteConnection { FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr) throws ZygoteInit.MethodAndArgsCaller { + /** + * By the time we get here, the native code has closed the two actual Zygote + * socket connections, and substituted /dev/null in their place. The LocalSocket + * objects still need to be closed properly. + */ + closeSocket(); ZygoteInit.closeServerSocket(); @@ -942,7 +963,7 @@ class ZygoteConnection { 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 9dc9116..5ce658b 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -16,27 +16,28 @@ package com.android.internal.os; -import static libcore.io.OsConstants.S_IRWXG; -import static libcore.io.OsConstants.S_IRWXO; +import static android.system.OsConstants.S_IRWXG; +import static android.system.OsConstants.S_IRWXO; import android.content.res.Resources; import android.content.res.TypedArray; import android.net.LocalServerSocket; import android.opengl.EGL14; +import android.os.Build; import android.os.Debug; import android.os.Process; import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; import android.util.EventLog; import android.util.Log; import dalvik.system.VMRuntime; -import dalvik.system.Zygote; import libcore.io.IoUtils; -import libcore.io.Libcore; -import libcore.io.OsConstants; import java.io.BufferedReader; import java.io.FileDescriptor; @@ -65,7 +66,7 @@ public class ZygoteInit { 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 +74,9 @@ public class ZygoteInit { /** 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 +153,15 @@ public class ZygoteInit { * * @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 +178,9 @@ public class ZygoteInit { * 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); @@ -195,18 +197,28 @@ public class ZygoteInit { FileDescriptor fd = sServerSocket.getFileDescriptor(); sServerSocket.close(); if (fd != null) { - Libcore.os.close(fd); + Os.close(fd); } } } catch (IOException ex) { Log.e(TAG, "Zygote: error closing sockets", ex); - } catch (libcore.io.ErrnoException ex) { + } catch (ErrnoException ex) { Log.e(TAG, "Zygote: error closing descriptor", ex); } sServerSocket = null; } + /** + * Return the server socket's underlying file descriptor, so that + * ZygoteConnection can pass it to the native code for proper + * closure after a child process is forked off. + */ + + static FileDescriptor getServerSocketFileDescriptor() { + return sServerSocket.getFileDescriptor(); + } + private static final int UNPRIVILEGED_UID = 9999; private static final int UNPRIVILEGED_GID = 9999; @@ -234,9 +246,11 @@ public class ZygoteInit { } static void preload() { + Log.d(TAG, "begin preload"); preloadClasses(); preloadResources(); preloadOpenGL(); + Log.d(TAG, "end preload"); } private static void preloadOpenGL() { @@ -469,7 +483,7 @@ public class ZygoteInit { closeServerSocket(); // set umask to 0077 so new files and directories will default to owner-only permissions. - Libcore.os.umask(S_IRWXG | S_IRWXO); + Os.umask(S_IRWXG | S_IRWXO); if (parsedArgs.niceName != null) { Process.setArgV0(parsedArgs.niceName); @@ -492,7 +506,7 @@ public class ZygoteInit { /** * Prepare the arguments and fork for the system server process. */ - private static boolean startSystemServer() + private static boolean startSystemServer(String abiList, String socketName) throws MethodAndArgsCaller, RuntimeException { long capabilities = posixCapabilitiesAsBits( OsConstants.CAP_BLOCK_SUSPEND, @@ -540,6 +554,10 @@ public class ZygoteInit { /* For child process */ if (pid == 0) { + if (hasSecondZygote(abiList)) { + waitForSecondaryZygote(socketName); + } + handleSystemServerProcess(parsedArgs); } @@ -565,7 +583,26 @@ public class ZygoteInit { // 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(); @@ -582,20 +619,12 @@ public class ZygoteInit { // 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")) { - startSystemServer(); - } else if (!argv[1].equals("")) { - throw new RuntimeException(argv[0] + USAGE_STRING); + if (startSystemServer) { + startSystemServer(abiList, socketName); } Log.i(TAG, "Accepting command socket connections"); - - runSelectLoop(); + runSelectLoop(abiList); closeServerSocket(); } catch (MethodAndArgsCaller caller) { @@ -608,6 +637,36 @@ public class ZygoteInit { } /** + * Return {@code true} if this device configuration has another zygote. + * + * We determine this by comparing the device ABI list with this zygotes + * list. If this zygote supports all ABIs this device supports, there won't + * be another zygote. + */ + private static boolean hasSecondZygote(String abiList) { + return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList); + } + + private static void waitForSecondaryZygote(String socketName) { + String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ? + Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET; + while (true) { + try { + final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName); + zs.close(); + break; + } catch (IOException ioe) { + Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage()); + } + + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + } + } + } + + /** * Runs the zygote process's select loop. Accepts new connections as * they happen, and reads commands from connections one spawn-request's * worth at a time. @@ -615,7 +674,7 @@ public class ZygoteInit { * @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]; @@ -654,7 +713,7 @@ public class ZygoteInit { 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/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java index 26235f1..71550be 100644 --- a/core/java/com/android/internal/util/FileRotator.java +++ b/core/java/com/android/internal/util/FileRotator.java @@ -336,7 +336,12 @@ public class FileRotator { final long deleteBefore = currentTimeMillis - mDeleteAgeMillis; final FileInfo info = new FileInfo(mPrefix); - for (String name : mBasePath.list()) { + String[] baseFiles = mBasePath.list(); + if (baseFiles == null) { + return; + } + + for (String name : baseFiles) { if (!info.parse(name)) continue; if (info.isActive()) { |
