Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.incrementalinstall; | 5 package org.chromium.incrementalinstall; |
| 6 | 6 |
| 7 import android.app.Application; | 7 import android.app.Application; |
| 8 import android.app.Instrumentation; | |
| 9 import android.content.ComponentName; | |
| 8 import android.content.Context; | 10 import android.content.Context; |
| 9 import android.content.pm.ApplicationInfo; | 11 import android.content.pm.ApplicationInfo; |
| 10 import android.content.pm.PackageManager; | 12 import android.content.pm.PackageManager; |
| 11 import android.content.pm.PackageManager.NameNotFoundException; | 13 import android.content.pm.PackageManager.NameNotFoundException; |
| 14 import android.os.Bundle; | |
| 12 import android.util.Log; | 15 import android.util.Log; |
| 13 | 16 |
| 14 import java.io.File; | 17 import java.io.File; |
| 15 import java.lang.ref.WeakReference; | 18 import java.lang.ref.WeakReference; |
| 16 import java.util.List; | 19 import java.util.List; |
| 17 import java.util.Map; | 20 import java.util.Map; |
| 18 | 21 |
| 19 /** | 22 /** |
| 20 * An Application that replaces itself with another Application (as defined in | 23 * An Application that replaces itself with another Application (as defined in |
| 21 * the "incremental-install-real-app" meta-data tag within the | 24 * the "incremental-install-real-app" meta-data tag within the |
| 22 * AndroidManifest.xml). It loads the other application only after side-loading | 25 * AndroidManifest.xml). It loads the other application only after side-loading |
| 23 * its .so and .dex files from /data/local/tmp. | 26 * its .so and .dex files from /data/local/tmp. |
| 24 */ | 27 */ |
| 25 public final class BootstrapApplication extends Application { | 28 public final class BootstrapApplication extends Application { |
| 26 private static final String TAG = "cr.incrementalinstall"; | 29 private static final String TAG = "cr.incrementalinstall"; |
| 27 private static final String MANAGED_DIR_PREFIX = "/data/local/tmp/incrementa l-app-"; | 30 private static final String MANAGED_DIR_PREFIX = "/data/local/tmp/incrementa l-app-"; |
| 28 private static final String REAL_APP_META_DATA_NAME = "incremental-install-r eal-app"; | 31 private static final String REAL_APP_META_DATA_NAME = "incremental-install-r eal-app"; |
| 32 private static final String REAL_INSTRUMENTATION_META_DATA_NAME = | |
| 33 "incremental-install-real-instrumentation"; | |
| 29 | 34 |
| 30 private ClassLoaderPatcher mClassLoaderPatcher; | 35 private ClassLoaderPatcher mClassLoaderPatcher; |
| 31 private Application mRealApplication; | 36 private Application mRealApplication; |
| 37 private Instrumentation mRealInstrumentation; | |
| 32 private Object mStashedProviderList; | 38 private Object mStashedProviderList; |
| 33 private Object mActivityThread; | 39 private Object mActivityThread; |
| 34 | 40 |
| 35 @Override | 41 @Override |
| 36 protected void attachBaseContext(Context context) { | 42 protected void attachBaseContext(Context context) { |
| 37 super.attachBaseContext(context); | 43 super.attachBaseContext(context); |
| 38 File incrementalRootDir = new File(MANAGED_DIR_PREFIX + context.getPacka geName()); | 44 File incrementalRootDir = new File(MANAGED_DIR_PREFIX + context.getPacka geName()); |
| 39 File libDir = new File(incrementalRootDir, "lib"); | 45 File libDir = new File(incrementalRootDir, "lib"); |
| 40 File dexDir = new File(incrementalRootDir, "dex"); | 46 File dexDir = new File(incrementalRootDir, "dex"); |
| 41 File installLockFile = new File(incrementalRootDir, "install.lock"); | 47 File installLockFile = new File(incrementalRootDir, "install.lock"); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 58 } | 64 } |
| 59 } | 65 } |
| 60 | 66 |
| 61 mClassLoaderPatcher.importNativeLibs(libDir); | 67 mClassLoaderPatcher.importNativeLibs(libDir); |
| 62 mClassLoaderPatcher.loadDexFiles(dexDir); | 68 mClassLoaderPatcher.loadDexFiles(dexDir); |
| 63 | 69 |
| 64 if (isFirstRun && mClassLoaderPatcher.mIsPrimaryProcess) { | 70 if (isFirstRun && mClassLoaderPatcher.mIsPrimaryProcess) { |
| 65 LockFile.clearInstallerLock(firstRunLockFile); | 71 LockFile.clearInstallerLock(firstRunLockFile); |
| 66 } | 72 } |
| 67 | 73 |
| 74 Bundle metadata = getManifestMetadata(); | |
| 75 if (Reflect.getField(mActivityThread, "mInstrumentationAppDir") != n ull) { | |
| 76 initInstrumentation(metadata.getString(REAL_INSTRUMENTATION_META _DATA_NAME)); | |
| 77 } else { | |
| 78 Log.i(TAG, "No instrumentation active."); | |
| 79 } | |
| 80 | |
| 81 // This allows us to detect the instrumentation.onCreate() call. | |
| 82 Reflect.setField(mActivityThread, "mInstrumentation", | |
| 83 new BootstrapInstrumentation(this)); | |
| 84 | |
| 68 // attachBaseContext() is called from ActivityThread#handleBindAppli cation() and | 85 // attachBaseContext() is called from ActivityThread#handleBindAppli cation() and |
| 69 // Application#mApplication is changed right after we return. Thus, we cannot swap | 86 // Application#mApplication is changed right after we return. Thus, we cannot swap |
| 70 // the Application instances until onCreate() is called. | 87 // the Application instances until onCreate() is called. |
| 71 String realApplicationName = getRealApplicationName(); | 88 String realApplicationName = metadata.getString(REAL_APP_META_DATA_N AME); |
| 72 Log.i(TAG, "Instantiating " + realApplicationName); | 89 Log.i(TAG, "Instantiating " + realApplicationName); |
| 73 mRealApplication = | 90 mRealApplication = |
| 74 (Application) Reflect.newInstance(Class.forName(realApplicat ionName)); | 91 (Application) Reflect.newInstance(Class.forName(realApplicat ionName)); |
| 75 Reflect.invokeMethod(mRealApplication, "attachBaseContext", context) ; | 92 Reflect.invokeMethod(mRealApplication, "attachBaseContext", context) ; |
| 76 | 93 |
| 77 // Between attachBaseContext() and onCreate(), ActivityThread tries to instantiate | 94 // Between attachBaseContext() and onCreate(), ActivityThread tries to instantiate |
| 78 // all ContentProviders. The ContentProviders break without the corr ect Application | 95 // all ContentProviders. The ContentProviders break without the corr ect Application |
| 79 // class being installed, so temporarily pretend there are no provid ers, and then | 96 // class being installed, so temporarily pretend there are no provid ers, and then |
| 80 // instantiate them explicitly within onCreate(). | 97 // instantiate them explicitly within onCreate(). |
| 81 disableContentProviders(); | 98 disableContentProviders(); |
| 82 Log.i(TAG, "Waiting for onCreate"); | 99 Log.i(TAG, "Waiting for onCreate"); |
| 83 } catch (Exception e) { | 100 } catch (Exception e) { |
| 84 throw new RuntimeException("Incremental install failed.", e); | 101 throw new RuntimeException("Incremental install failed.", e); |
| 85 } | 102 } |
| 86 } | 103 } |
| 87 | 104 |
| 105 /** | |
| 106 * Instantiates and initializes mRealInstrumentation (the real Instrumentati on class). | |
| 107 */ | |
| 108 private void initInstrumentation(String realInstrumentationName) | |
| 109 throws ReflectiveOperationException { | |
| 110 Log.i(TAG, "Instantiating instrumentation " + realInstrumentationName); | |
| 111 mRealInstrumentation = (Instrumentation) Reflect.newInstance( | |
| 112 Class.forName(realInstrumentationName)); | |
| 113 Instrumentation oldInstrumentation = | |
| 114 (Instrumentation) Reflect.getField(mActivityThread, "mInstrument ation"); | |
| 115 | |
| 116 // Initialize the fields that are set by Instrumentation.init(). | |
| 117 String[] initFields = {"mThread", "mMessageQueue", "mInstrContext", "mAp pContext", | |
| 118 "mWatcher", "mUiAutomationConnection"}; | |
| 119 for (String fieldName : initFields) { | |
| 120 Reflect.setField(mRealInstrumentation, fieldName, | |
| 121 Reflect.getField(oldInstrumentation, fieldName)); | |
| 122 } | |
| 123 // But make sure the correct ComponentName is used. | |
| 124 ComponentName newName = new ComponentName( | |
| 125 oldInstrumentation.getComponentName().getPackageName(), realInst rumentationName); | |
| 126 Reflect.setField(mRealInstrumentation, "mComponent", newName); | |
| 127 } | |
| 128 | |
| 129 /** | |
| 130 * Called by BootstrapInstrumentation from Instrumentation.onCreate(). | |
| 131 */ | |
| 132 void onInstrumentationCreate(Bundle arguments) { | |
|
nyquist
2015/10/15 19:05:15
As a layman it sounds like this method will only b
agrieve
2015/10/15 19:20:48
Left the name (as I couldn't think of a better one
| |
| 133 Log.i(TAG, "Instrumentation.onCreate() called. Swapping references."); | |
| 134 try { | |
| 135 swapApplicationReferences(); | |
| 136 enableContentProviders(); | |
| 137 if (mRealInstrumentation != null) { | |
| 138 Reflect.setField(mActivityThread, "mInstrumentation", mRealInstr umentation); | |
| 139 mRealInstrumentation.onCreate(arguments); | |
| 140 } | |
| 141 } catch (Exception e) { | |
| 142 throw new RuntimeException("Incremental install failed.", e); | |
| 143 } | |
| 144 } | |
| 145 | |
| 88 @Override | 146 @Override |
| 89 public void onCreate() { | 147 public void onCreate() { |
| 90 super.onCreate(); | 148 super.onCreate(); |
| 91 try { | 149 try { |
| 92 Log.i(TAG, "onCreate() called. Swapping Application references"); | 150 Log.i(TAG, "Application.onCreate() called."); |
| 93 swapApplicationReferences(); | |
| 94 enableContentProviders(); | |
| 95 Log.i(TAG, "Calling onCreate"); | |
| 96 mRealApplication.onCreate(); | 151 mRealApplication.onCreate(); |
| 97 } catch (Exception e) { | 152 } catch (Exception e) { |
| 98 throw new RuntimeException("Incremental install failed.", e); | 153 throw new RuntimeException("Incremental install failed.", e); |
| 99 } | 154 } |
| 100 } | 155 } |
| 101 | 156 |
| 102 /** | 157 /** |
| 103 * Returns the class name of the real Application class (recorded in the | 158 * Returns the class name of the real Application class (recorded in the |
| 104 * AndroidManifest.xml) | 159 * AndroidManifest.xml) |
| 105 */ | 160 */ |
| 106 private String getRealApplicationName() throws NameNotFoundException { | 161 private Bundle getManifestMetadata() throws NameNotFoundException { |
| 107 ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getPack ageName(), | 162 ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getPack ageName(), |
| 108 PackageManager.GET_META_DATA); | 163 PackageManager.GET_META_DATA); |
| 109 return appInfo.metaData.getString(REAL_APP_META_DATA_NAME); | 164 return appInfo.metaData; |
| 110 } | 165 } |
| 111 | 166 |
| 112 /** | 167 /** |
| 113 * Nulls out ActivityThread.mBoundApplication.providers. | 168 * Nulls out ActivityThread.mBoundApplication.providers. |
| 114 */ | 169 */ |
| 115 private void disableContentProviders() throws ReflectiveOperationException { | 170 private void disableContentProviders() throws ReflectiveOperationException { |
| 116 Object data = Reflect.getField(mActivityThread, "mBoundApplication"); | 171 Object data = Reflect.getField(mActivityThread, "mBoundApplication"); |
| 117 mStashedProviderList = Reflect.getField(data, "providers"); | 172 mStashedProviderList = Reflect.getField(data, "providers"); |
| 118 Reflect.setField(data, "providers", null); | 173 Reflect.setField(data, "providers", null); |
| 119 } | 174 } |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 158 for (Map.Entry<String, WeakReference<?>> entry : packageMap.entrySet ()) { | 213 for (Map.Entry<String, WeakReference<?>> entry : packageMap.entrySet ()) { |
| 159 Object loadedApk = entry.getValue().get(); | 214 Object loadedApk = entry.getValue().get(); |
| 160 if (loadedApk != null && Reflect.getField(loadedApk, "mApplicati on") == this) { | 215 if (loadedApk != null && Reflect.getField(loadedApk, "mApplicati on") == this) { |
| 161 Reflect.setField(loadedApk, "mApplication", mRealApplication ); | 216 Reflect.setField(loadedApk, "mApplication", mRealApplication ); |
| 162 Reflect.setField(mRealApplication, "mLoadedApk", loadedApk); | 217 Reflect.setField(mRealApplication, "mLoadedApk", loadedApk); |
| 163 } | 218 } |
| 164 } | 219 } |
| 165 } | 220 } |
| 166 } | 221 } |
| 167 } | 222 } |
| OLD | NEW |