From 5c1e00b14d2ef10ec76abf3e951fa8003a67f558 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 18 Jun 2009 17:10:57 -0700 Subject: Fix targetSdkVersion, make resize mode a flag, delayed dexopt, easy ApplicationInfo. - Fix a bug where targetSdkVersion could not be set if minSdkVersion. Stupid, stupid. Also make sure to fail if minSdkVersion is for a code name. Really stupid. - Change the API for resize compatibility mode to be a bit in the flags field, instead of a separate boolean. - Implement delayed dexopting, to avoid the looong full dexopt during boot. This is only enabled for "eng" builds. When in this mode, the activity manager will make sure that a dexopt has been done before loading an .apk into a process, and will try to avoid displaying ANRs if they are due to the dexopt causing some operation to take longer than it normally would (though I make no guarantees about this totally working). - Add API to Context to get the ApplicationInfo for its package, for easy access to things like targetSdkVersion. --- api/current.xml | 65 ++++++++++----- core/java/android/app/ActivityThread.java | 6 +- core/java/android/app/ApplicationContext.java | 8 ++ core/java/android/content/Context.java | 4 + core/java/android/content/ContextWrapper.java | 6 ++ core/java/android/content/pm/ApplicationInfo.java | 19 ++--- core/java/android/content/pm/IPackageManager.aidl | 7 ++ core/java/android/content/pm/PackageManager.java | 6 -- core/java/android/content/pm/PackageParser.java | 50 +++++++----- .../android/content/res/CompatibilityInfo.java | 3 +- .../com/android/server/PackageManagerService.java | 92 ++++++++++++++++------ .../android/server/am/ActivityManagerService.java | 62 ++++++++++++++- .../java/com/android/server/am/HistoryRecord.java | 6 ++ test-runner/android/test/mock/MockContext.java | 6 ++ .../android/layoutlib/bridge/BridgeContext.java | 7 ++ 15 files changed, 263 insertions(+), 84 deletions(-) diff --git a/api/current.xml b/api/current.xml index 4efc71f..95c396f 100644 --- a/api/current.xml +++ b/api/current.xml @@ -27473,6 +27473,17 @@ visibility="public" > + + + + + + - - - - + + mSdkVersion) { + outError[0] = "Requires newer sdk version #" + minVers + + " (current version is #" + mSdkVersion + ")"; + mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; + return null; + } + if (targetCode != null) { if (!targetCode.equals(mSdkCodename)) { if (mSdkCodename != null) { @@ -817,13 +836,6 @@ public class PackageParser { pkg.applicationInfo.targetSdkVersion = targetVers; } - if (minVers > mSdkVersion) { - outError[0] = "Requires newer sdk version #" + minVers - + " (current version is #" + mSdkVersion + ")"; - mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; - return null; - } - if (maxVers < mSdkVersion) { outError[0] = "Requires older sdk version #" + maxVers + " (current version is #" + mSdkVersion + ")"; @@ -865,7 +877,7 @@ public class PackageParser { XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("expandable")) { - pkg.expandable = true; + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS; XmlUtils.skipCurrentTag(parser); } else { Log.w(TAG, "Bad element under : " @@ -2262,9 +2274,6 @@ public class PackageParser { public final ArrayList supportsDensityList = new ArrayList(); public int[] supportsDensities = null; - // If the application's window is expandable. - public boolean expandable; - // If this is a 3rd party app, this is the path of the zip file. public String mPath; @@ -2287,6 +2296,17 @@ public class PackageParser { // preferred up order. public int mPreferredOrder = 0; + // For use by package manager service to keep track of which apps + // have been installed with forward locking. + public boolean mForwardLocked; + + // 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; + // Additional data supplied by callers. public Object mExtras; @@ -2439,9 +2459,6 @@ public class PackageParser { && p.supportsDensities != null) { return true; } - if ((flags & PackageManager.GET_EXPANDABLE) != 0) { - return true; - } return false; } @@ -2462,9 +2479,6 @@ public class PackageParser { if ((flags & PackageManager.GET_SUPPORTS_DENSITIES) != 0) { ai.supportsDensities = p.supportsDensities; } - if ((flags & PackageManager.GET_EXPANDABLE) != 0) { - ai.expandable = p.expandable; - } return ai; } diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java index 836de39..680fef8 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -69,7 +69,8 @@ public class CompatibilityInfo { public final boolean mScalingRequired; public CompatibilityInfo(ApplicationInfo appInfo) { - mExpandable = mConfiguredExpandable = appInfo.expandable; + mExpandable = mConfiguredExpandable = + (appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0; float packageDensityScale = -1.0f; if (appInfo.supportsDensities != null) { diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index f51f3d0..f18d6e0 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -19,7 +19,6 @@ package com.android.server; import com.android.internal.app.ResolverActivity; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; -import com.android.server.PackageManagerService.PreferredActivity; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -30,7 +29,6 @@ import android.app.IActivityManager; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -152,6 +150,7 @@ class PackageManagerService extends IPackageManager.Stub { final Context mContext; final boolean mFactoryTest; + final boolean mNoDexOpt; final DisplayMetrics mMetrics; final int mDefParseFlags; final String[] mSeparateProcesses; @@ -303,6 +302,7 @@ class PackageManagerService extends IPackageManager.Stub { mContext = context; mFactoryTest = factoryTest; + mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type")); mMetrics = new DisplayMetrics(); mSettings = new Settings(); mSettings.addSharedUserLP("android.uid.system", @@ -372,6 +372,10 @@ class PackageManagerService extends IPackageManager.Stub { startTime); int scanMode = SCAN_MONITOR; + if (mNoDexOpt) { + Log.w(TAG, "Running ENG build: no pre-dexopt!"); + scanMode |= SCAN_NO_DEX; + } final HashSet libFiles = new HashSet(); @@ -993,10 +997,11 @@ class PackageManagerService extends IPackageManager.Stub { if (Config.LOGV) Log.v(TAG, "getActivityInfo " + component + ": " + a); if (a != null && mSettings.isEnabledLP(a.info, flags)) { ActivityInfo ainfo = PackageParser.generateActivityInfo(a, flags); - if (ainfo != null && (flags & PackageManager.GET_EXPANDABLE) != 0) { + if (ainfo != null) { ApplicationInfo appInfo = getApplicationInfo(component.getPackageName(), - PackageManager.GET_EXPANDABLE | PackageManager.GET_SUPPORTS_DENSITIES); - if (appInfo != null && !appInfo.expandable) { + PackageManager.GET_SUPPORTS_DENSITIES); + if (appInfo != null && + (appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) == 0) { // Check if the screen size is same as what the application expect. CompatibilityInfo info = new CompatibilityInfo(appInfo); DisplayMetrics metrics = new DisplayMetrics(); @@ -1009,11 +1014,13 @@ class PackageManagerService extends IPackageManager.Stub { // Don't allow an app that cannot expand to handle rotation. ainfo.configChanges &= ~ ActivityInfo.CONFIG_ORIENTATION; } else { - appInfo.expandable = true; + appInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS; } if (DEBUG_SETTINGS) { Log.d(TAG, "component=" + component + - ", expandable:" + appInfo.expandable); + ", expandable:" + + ((appInfo.flags & + ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0)); } } } @@ -1943,7 +1950,56 @@ class PackageManagerService extends IPackageManager.Stub { } return true; } + + public boolean performDexOpt(String packageName) { + if (!mNoDexOpt) { + return false; + } + + PackageParser.Package p; + synchronized (mPackages) { + p = mPackages.get(packageName); + if (p == null || p.mDidDexOpt) { + return false; + } + } + synchronized (mInstallLock) { + return performDexOptLI(p, false) == DEX_OPT_PERFORMED; + } + } + + static final int DEX_OPT_SKIPPED = 0; + static final int DEX_OPT_PERFORMED = 1; + static final int DEX_OPT_FAILED = -1; + + private int performDexOptLI(PackageParser.Package pkg, boolean forceDex) { + boolean performed = false; + if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { + String path = pkg.mScanPath; + int ret = 0; + try { + if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) { + ret = mInstaller.dexopt(path, pkg.applicationInfo.uid, + !pkg.mForwardLocked); + pkg.mDidDexOpt = true; + performed = true; + } + } catch (FileNotFoundException e) { + Log.w(TAG, "Apk not found for dexopt: " + path); + ret = -1; + } catch (IOException e) { + Log.w(TAG, "Exception reading apk: " + path, e); + ret = -1; + } + if (ret < 0) { + //error from installer + return DEX_OPT_FAILED; + } + } + return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED; + } + private PackageParser.Package scanPackageLI( File scanFile, File destCodeFile, File destResourceFile, PackageParser.Package pkg, int parseFlags, int scanMode) { @@ -2239,23 +2295,11 @@ class PackageManagerService extends IPackageManager.Stub { } } - if ((scanMode&SCAN_NO_DEX) == 0 - && (pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { - int ret = 0; - try { - if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) { - ret = mInstaller.dexopt(path, pkg.applicationInfo.uid, - (scanMode&SCAN_FORWARD_LOCKED) == 0); - } - } catch (FileNotFoundException e) { - Log.w(TAG, "Apk not found for dexopt: " + path); - ret = -1; - } catch (IOException e) { - Log.w(TAG, "Exception reading apk: " + path, e); - ret = -1; - } - if (ret < 0) { - //error from installer + pkg.mForwardLocked = (scanMode&SCAN_FORWARD_LOCKED) != 0; + pkg.mScanPath = path; + + if ((scanMode&SCAN_NO_DEX) == 0) { + if (performDexOptLI(pkg, forceDex) == DEX_OPT_FAILED) { mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; return null; } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 045e636..8cf1cc2 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -181,7 +181,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // The flags that are set for all calls we make to the package manager. static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES - | PackageManager.GET_SUPPORTS_DENSITIES | PackageManager.GET_EXPANDABLE; + | PackageManager.GET_SUPPORTS_DENSITIES; private static final String SYSTEM_SECURE = "ro.secure"; @@ -809,6 +809,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen */ int[] mProcDeaths = new int[20]; + /** + * This is set if we had to do a delayed dexopt of an app before launching + * it, to increasing the ANR timeouts in that case. + */ + boolean mDidDexOpt; + String mDebugApp = null; boolean mWaitForDebugger = false; boolean mDebugTransient = false; @@ -1007,6 +1013,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen processNextBroadcast(true); } break; case BROADCAST_TIMEOUT_MSG: { + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG); + mHandler.sendMessageDelayed(nmsg, BROADCAST_TIMEOUT); + return; + } broadcastTimeout(); } break; case PAUSE_TIMEOUT_MSG: { @@ -1017,9 +1029,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen activityPaused(token, null, true); } break; case IDLE_TIMEOUT_MSG: { - IBinder token = (IBinder)msg.obj; + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG); + nmsg.obj = msg.obj; + mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT); + return; + } // We don't at this point know if the activity is fullscreen, // so we need to be conservative and assume it isn't. + IBinder token = (IBinder)msg.obj; Log.w(TAG, "Activity idle timeout for " + token); activityIdleInternal(token, true); } break; @@ -1035,6 +1054,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen activityIdle(token); } break; case SERVICE_TIMEOUT_MSG: { + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG); + nmsg.obj = msg.obj; + mHandler.sendMessageDelayed(nmsg, SERVICE_TIMEOUT); + return; + } serviceTimeout((ProcessRecord)msg.obj); } break; case UPDATE_TIME_ZONE: { @@ -1071,6 +1097,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } break; case LAUNCH_TIMEOUT_MSG: { + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG); + mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT); + return; + } synchronized (ActivityManagerService.this) { if (mLaunchingActivity.isHeld()) { Log.w(TAG, "Launch timeout has expired, giving up wake lock!"); @@ -1091,6 +1123,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } case PROC_START_TIMEOUT_MSG: { + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG); + nmsg.obj = msg.obj; + mHandler.sendMessageDelayed(nmsg, PROC_START_TIMEOUT); + return; + } ProcessRecord app = (ProcessRecord)msg.obj; synchronized (ActivityManagerService.this) { processStartTimedOutLocked(app); @@ -1607,6 +1646,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return proc; } + private void ensurePackageDexOpt(String packageName) { + IPackageManager pm = ActivityThread.getPackageManager(); + try { + if (pm.performDexOpt(packageName)) { + mDidDexOpt = true; + } + } catch (RemoteException e) { + } + } + private boolean isNextTransitionForward() { int transit = mWindowManager.getPendingAppTransition(); return transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN @@ -1666,6 +1715,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (r.isHomeActivity) { mHomeProcess = app; } + ensurePackageDexOpt(r.intent.getComponent().getPackageName()); app.thread.scheduleLaunchActivity(new Intent(r.intent), r, r.info, r.icicle, results, newIntents, !andResume, isNextTransitionForward()); @@ -4819,6 +4869,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE) || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL); } + ensurePackageDexOpt(app.info.packageName); + if (app.instrumentationInfo != null) { + ensurePackageDexOpt(app.instrumentationInfo.packageName); + } thread.bindApplication(processName, app.instrumentationInfo != null ? app.instrumentationInfo : app.info, providers, app.instrumentationClass, app.instrumentationProfileFile, @@ -4907,6 +4961,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Check whether the next backup agent is in this process... if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.info.uid) { if (DEBUG_BACKUP) Log.v(TAG, "New app is backup target, launching agent for " + app); + ensurePackageDexOpt(mBackupTarget.appInfo.packageName); try { thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, mBackupTarget.backupMode); } catch (Exception e) { @@ -6918,6 +6973,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } app.pubProviders.put(cpi.name, cpr); app.addPackage(cpi.applicationInfo.packageName); + ensurePackageDexOpt(cpi.applicationInfo.packageName); } } return providers; @@ -9542,6 +9598,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (r.stats.getBatteryStats()) { r.stats.startLaunchedLocked(); } + ensurePackageDexOpt(r.serviceInfo.packageName); app.thread.scheduleCreateService(r, r.serviceInfo); created = true; } finally { @@ -11098,6 +11155,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Delivering to component " + r.curComponent + ": " + r); + ensurePackageDexOpt(r.intent.getComponent().getPackageName()); app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver, r.resultCode, r.resultData, r.resultExtras, r.ordered); started = true; diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java index 944ea02..b3fc313 100644 --- a/services/java/com/android/server/am/HistoryRecord.java +++ b/services/java/com/android/server/am/HistoryRecord.java @@ -463,6 +463,12 @@ class HistoryRecord extends IApplicationToken.Stub { return false; } + if (service.mDidDexOpt) { + // Give more time since we were dexopting. + service.mDidDexOpt = false; + return false; + } + if (r.app.instrumentationClass == null) { service.appNotRespondingLocked(r.app, r, "keyDispatchingTimedOut"); } else { diff --git a/test-runner/android/test/mock/MockContext.java b/test-runner/android/test/mock/MockContext.java index 9e0cf2c..efc4880 100644 --- a/test-runner/android/test/mock/MockContext.java +++ b/test-runner/android/test/mock/MockContext.java @@ -24,6 +24,7 @@ import android.content.IntentFilter; import android.content.BroadcastReceiver; import android.content.ServiceConnection; import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; @@ -100,6 +101,11 @@ public class MockContext extends Context { } @Override + public ApplicationInfo getApplicationInfo() { + throw new UnsupportedOperationException(); + } + + @Override public String getPackageResourcePath() { throw new UnsupportedOperationException(); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java index d0896b5..69f3d9c 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java @@ -29,6 +29,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Configuration; @@ -960,6 +961,12 @@ public final class BridgeContext extends Context { } @Override + public ApplicationInfo getApplicationInfo() { + // TODO Auto-generated method stub + return null; + } + + @Override public String getPackageResourcePath() { // TODO Auto-generated method stub return null; -- cgit v1.1