Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(195)

Side by Side Diff: build/android/incremental_install/java/org/chromium/incrementalinstall/BootstrapApplication.java

Issue 1338813003: GN: Side-load dex files as well as native code in incremental installs (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Python review comments 1 Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 package org.chromium.incrementalinstall;
nyquist 2015/09/16 06:30:45 Optional nit: How do you feel about org.chromium.b
agrieve 2015/09/16 14:59:58 All the same to me, so will leave it if you don't
6
7 import android.app.Application;
8 import android.content.Context;
9 import android.content.pm.ApplicationInfo;
10 import android.content.pm.PackageManager;
11 import android.content.pm.PackageManager.NameNotFoundException;
12 import android.util.Log;
13
14 import java.io.File;
15 import java.lang.ref.WeakReference;
16 import java.lang.reflect.InvocationTargetException;
17 import java.util.List;
18 import java.util.Map;
19
20 /**
21 * An Application that replaces itself with another Application (as defined in
22 * the "incremental-install-real-app" meta-data tag within the
23 * AndroidManifest.xml). It loads the other application only after side-loading
24 * its .so and .dex files from /data/local/tmp.
25 */
26 public final class BootstrapApplication extends Application {
27 private static final String TAG = "cr.incrementalinstall";
28 private static final String MANAGED_DIR_PREFIX = "/data/local/tmp/incrementa l-app-";
29 private static final String REAL_APP_META_DATA_NAME = "incremental-install-r eal-app";
30
31 private ClassLoaderPatcher mClassLoaderPatcher;
32 private Application mRealApplication;
33 private Object mStashedProviderList;
34 private Object mActivityThread;
35
36 @Override
37 protected void attachBaseContext(Context context) {
38 super.attachBaseContext(context);
39 File incrementalRootDir = new File(MANAGED_DIR_PREFIX + context.getPacka geName());
40 File libDir = new File(incrementalRootDir, "lib");
41 File dexDir = new File(incrementalRootDir, "dex");
42 File installLockFile = new File(incrementalRootDir, "install.lock");
43 File firstRunLockFile = new File(incrementalRootDir, "firstrun.lock");
44
45 try {
46 mActivityThread = Reflect.invokeMethod(Class.forName("android.app.Ac tivityThread"),
47 "currentActivityThread");
48 mClassLoaderPatcher = new ClassLoaderPatcher(context);
49
50 boolean isFirstRun = LockFile.installerLockExists(firstRunLockFile);
51 if (isFirstRun && mClassLoaderPatcher.mIsPrimaryProcess) {
nyquist 2015/09/16 06:30:45 Nit: I know it would be an additional indent, but
agrieve 2015/09/16 14:59:58 Done.
52 // Wait for incremental_install.py to finish.
53 LockFile.waitForInstallerLock(installLockFile, 20 * 1000);
54 } else if (isFirstRun) {
55 // Wait for the browser process to create the optimized dex file s
56 // (and for M+, copy the library files).
57 LockFile.waitForInstallerLock(firstRunLockFile, 30 * 1000);
58 }
59
60 mClassLoaderPatcher.importNativeLibs(libDir);
61 mClassLoaderPatcher.loadDexFiles(dexDir);
62
63 if (isFirstRun && mClassLoaderPatcher.mIsPrimaryProcess) {
64 LockFile.clearInstallerLock(firstRunLockFile);
65 }
66
67 // attachBaseContext() is called from ActivityThread#handleBindAppli cation() and
68 // Application#mApplication is changed right after we return. Thus, we cannot swap
69 // the Application instances until onCreate() is called.
70 String mRealApplicationName = getRealApplicationName();
nyquist 2015/09/16 06:30:45 Nit: Just |realApplicationName|
agrieve 2015/09/16 14:59:58 Done.
71 Log.i(TAG, "Instantiating " + mRealApplicationName);
72 mRealApplication =
73 (Application) Reflect.newInstance(Class.forName(mRealApplica tionName));
74 Reflect.invokeMethod(mRealApplication, "attachBaseContext", context) ;
75
76 // Between attachBaseContext() and onCreate(), ActivityThread tries to instantiate
77 // all ContentProviders. The ContentProviders break without the corr ect Application
78 // class being installed, so temporarily pretend there are no provid ers, and then
79 // instantiate them explicitly within onCreate().
80 disableContentProviders();
81 Log.i(TAG, "Waiting for onCreate");
82 } catch (Exception e) {
83 throw new RuntimeException(e);
84 }
85 }
86
87 @Override
88 public void onCreate() {
89 super.onCreate();
90 try {
91 Log.i(TAG, "onCreate() called. Swapping Application references");
92 swapApplicationReferences();
93 enableContentProviders();
94 Log.i(TAG, "Calling onCreate");
95 mRealApplication.onCreate();
96 } catch (Exception e) {
97 throw new RuntimeException(e);
nyquist 2015/09/16 06:30:45 Nit: I know we log that we're trying above here, b
agrieve 2015/09/16 14:59:58 Done.
98 }
99 }
100
101 /**
102 * Returns the class name of the real Application class (recorded in the
103 * AndroidManifest.xml)
104 */
105 private String getRealApplicationName() throws NameNotFoundException {
106 ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getPack ageName(),
107 PackageManager.GET_META_DATA);
108 return appInfo.metaData.getString(REAL_APP_META_DATA_NAME);
109 }
110
111 /**
112 * Nulls out ActivityThread.mBoundApplication.providers.
113 */
114 private void disableContentProviders() throws NoSuchFieldException {
115 Object data = Reflect.getField(mActivityThread, "mBoundApplication");
116 mStashedProviderList = Reflect.getField(data, "providers");
117 Reflect.setField(data, "providers", null);
118 }
119
120 /**
121 * Restores the value of ActivityThread.mBoundApplication.providers, and inv okes
122 * ActivityThread#installContentProviders().
123 */
124 private void enableContentProviders() throws NoSuchMethodException, Invocati onTargetException,
125 NoSuchFieldException {
126 Object data = Reflect.getField(mActivityThread, "mBoundApplication");
127 Reflect.setField(data, "providers", mStashedProviderList);
128 if (mStashedProviderList != null && mClassLoaderPatcher.mIsPrimaryProces s) {
129 Log.i(TAG, "Instantiating content providers");
130 Reflect.invokeMethod(mActivityThread, "installContentProviders", mRe alApplication,
131 mStashedProviderList);
132 }
133 mStashedProviderList = null;
134 }
135
136 /**
137 * Changes all fields within framework classes that have stored an reference to this
138 * BootstrapApplication to instead store references to mRealApplication.
139 * @throws NoSuchFieldException
140 */
141 @SuppressWarnings("unchecked")
142 private void swapApplicationReferences() throws NoSuchFieldException {
143 if (Reflect.getField(mActivityThread, "mInitialApplication") == this) {
144 Reflect.setField(mActivityThread, "mInitialApplication", mRealApplic ation);
145 }
146
147 List<Application> allApplications =
148 (List<Application>) Reflect.getField(mActivityThread, "mAllAppli cations");
149 for (int i = 0; i < allApplications.size(); i++) {
150 if (allApplications.get(i) == this) {
151 allApplications.set(i, mRealApplication);
152 }
153 }
154
155 for (String fieldName : new String[] { "mPackages", "mResourcePackages" }) {
156 Map<String, WeakReference<?>> packageMap =
157 (Map<String, WeakReference<?>>) Reflect.getField(mActivityTh read, fieldName);
158 for (Map.Entry<String, WeakReference<?>> entry : packageMap.entrySet ()) {
159 Object loadedApk = entry.getValue().get();
160 if (loadedApk != null && Reflect.getField(loadedApk, "mApplicati on") == this) {
161 Reflect.setField(loadedApk, "mApplication", mRealApplication );
162 Reflect.setField(mRealApplication, "mLoadedApk", loadedApk);
163 }
164 }
165 }
166 }
167 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698