2222import static com .tencent .tinker .loader .shareutil .ShareReflectUtil .findField ;
2323import static com .tencent .tinker .loader .shareutil .ShareReflectUtil .findMethod ;
2424
25- import android .app .Application ;
2625import android .content .Context ;
2726import android .content .pm .ApplicationInfo ;
2827import android .content .res .AssetManager ;
@@ -57,6 +56,7 @@ class TinkerResourcePatcher {
5756
5857 private static Map <Object , WeakReference <Object >> resourceImpls = null ;
5958 private static Object currentActivityThread = null ;
59+ private static AssetManager newAssetManager = null ;
6060
6161 // method
6262 private static Constructor <?> newAssetManagerCtor = null ;
@@ -66,11 +66,8 @@ class TinkerResourcePatcher {
6666
6767 // field
6868 private static Field assetsFiled = null ;
69- private static Field resourcesImplField = null ;
69+ private static Field resourcesImplFiled = null ;
7070 private static Field resDir = null ;
71- private static Field resources = null ;
72- private static Field application = null ;
73- private static Field classloader = null ;
7471 private static Field packagesFiled = null ;
7572 private static Field resourcePackagesFiled = null ;
7673 private static Field publicSourceDirField = null ;
@@ -101,9 +98,6 @@ public static void isResourceCanPatch(Context context) throws Throwable {
10198 }
10299
103100 resDir = findField (loadedApkClass , "mResDir" );
104- resources = findField (loadedApkClass , "mResources" );
105- application = findField (loadedApkClass , "mApplication" );
106- classloader = findField (loadedApkClass , "mClassLoader" );
107101 packagesFiled = findField (activityThread , "mPackages" );
108102 try {
109103 resourcePackagesFiled = findField (activityThread , "mResourcePackages" );
@@ -175,7 +169,7 @@ public static void isResourceCanPatch(Context context) throws Throwable {
175169 if (SDK_INT >= 24 ) {
176170 try {
177171 // N moved the mAssets inside an mResourcesImpl field
178- resourcesImplField = findField (resources , "mResourcesImpl" );
172+ resourcesImplFiled = findField (resources , "mResourcesImpl" );
179173 } catch (Throwable ignore ) {
180174 // for safety
181175 assetsFiled = findField (resources , "mAssets" );
@@ -192,32 +186,21 @@ public static void isResourceCanPatch(Context context) throws Throwable {
192186 }
193187
194188 /**
195- * @param app
189+ * @param context
196190 * @param externalResourceFile
197191 * @throws Throwable
198192 */
199193 @ SuppressWarnings ({"ConstantConditions" , "unchecked" })
200- public static void monkeyPatchExistingResources (Application app , String externalResourceFile , boolean isReInject ) throws Throwable {
194+ public static void monkeyPatchExistingResources (Context context , String externalResourceFile , boolean isReInject ) throws Throwable {
201195 if (externalResourceFile == null ) {
202196 return ;
203197 }
204198
205- final ApplicationInfo appInfo = app .getApplicationInfo ();
199+ final ApplicationInfo appInfo = context .getApplicationInfo ();
206200
207- // Ensure mPackages and mResourcePackages cache contains LoadedApk instance related to this app.
208- packageContext = app .createPackageContext (app .getPackageName (), Context .CONTEXT_INCLUDE_CODE );
209- packageResContext = app .createPackageContext (app .getPackageName (), 0 );
210-
211- final Resources oldResources = packageResContext .getResources ();
212- Field implAssetsField = null ;
213- Object appAssetManager = null ;
214- if (resourcesImplField != null ) {
215- final Object appResourcesImpl = resourcesImplField .get (oldResources );
216- implAssetsField = findField (appResourcesImpl , "mAssets" );
217- appAssetManager = implAssetsField .get (appResourcesImpl );
218- } else {
219- appAssetManager = assetsFiled .get (oldResources );
220- }
201+ // Prevent cached LoadedApk being recycled.
202+ packageContext = context .createPackageContext (context .getPackageName (), Context .CONTEXT_INCLUDE_CODE );
203+ packageResContext = context .createPackageContext (context .getPackageName (), 0 );
221204
222205 final Field [] packagesFields = new Field []{packagesFiled , resourcePackagesFiled };
223206 for (Field field : packagesFields ) {
@@ -235,29 +218,35 @@ public static void monkeyPatchExistingResources(Application app, String external
235218 final String resDirPath = (String ) resDir .get (loadedApk );
236219 if (appInfo .sourceDir .equals (resDirPath )) {
237220 resDir .set (loadedApk , externalResourceFile );
238- if (isReInject ) {
239- application .set (loadedApk , app );
240- classloader .set (loadedApk , SystemClassLoaderAdder .getInjectedClassLoader ());
241- }
242- resources .set (loadedApk , null );
243221 }
244222 }
245223 }
246224
247- // Let modified resDir take effect and use it to create new Context instance.
248- packageContext = app .createPackageContext (app .getPackageName (), Context .CONTEXT_INCLUDE_CODE );
249- packageResContext = app .createPackageContext (app .getPackageName (), 0 );
225+ if (isReInject ) {
226+ ShareTinkerLog .i (TAG , "Re-injecting, skip rest logic." );
227+ PatchedResourcesInsuranceLogic .recordCurrentPatchedResModifiedTime (externalResourceFile );
228+ return ;
229+ }
230+
231+ newAssetManager = (AssetManager ) newAssetManagerCtor .newInstance ();
232+ // Create a new AssetManager instance and point it to the resources installed under
233+ if (((Integer ) addAssetPathMethod .invoke (newAssetManager , externalResourceFile )) == 0 ) {
234+ throw new IllegalStateException ("Could not create new AssetManager" );
235+ }
250236 PatchedResourcesInsuranceLogic .recordCurrentPatchedResModifiedTime (externalResourceFile );
251237
252- final Resources newResources = packageResContext .getResources ();
253- Object newAssetManager = null ;
254- if (resourcesImplField != null ) {
255- // N
256- final Object resourceImpl = resourcesImplField .get (newResources );
257- newAssetManager = implAssetsField .get (resourceImpl );
258- } else {
259- //pre-N
260- newAssetManager = assetsFiled .get (newResources );
238+ // Add SharedLibraries to AssetManager for resolve system resources not found issue
239+ // This influence SharedLibrary Package ID
240+ if (shouldAddSharedLibraryAssets (appInfo )) {
241+ for (String sharedLibrary : appInfo .sharedLibraryFiles ) {
242+ if (!sharedLibrary .endsWith (".apk" )) {
243+ continue ;
244+ }
245+ if (((Integer ) addAssetPathAsSharedLibraryMethod .invoke (newAssetManager , sharedLibrary )) == 0 ) {
246+ throw new IllegalStateException ("AssetManager add SharedLibrary Fail" );
247+ }
248+ ShareTinkerLog .i (TAG , "addAssetPathAsSharedLibrary " + sharedLibrary );
249+ }
261250 }
262251
263252 // Kitkat needs this method call, Lollipop doesn't. However, it doesn't seem to cause any harm
@@ -273,65 +262,55 @@ public static void monkeyPatchExistingResources(Application app, String external
273262 continue ;
274263 }
275264 // Set the AssetManager of the Resources instance to our brand new one
276- if (resourcesImplField != null ) {
277- // N
278- final Object resourcesImpl = resourcesImplField .get (resources );
279- final Object assetManager = implAssetsField .get (resourcesImpl );
280- if (assetManager == appAssetManager ) {
281- implAssetsField .set (resourcesImpl , newAssetManager );
282- }
283- } else {
265+ try {
284266 //pre-N
285- final Object assetManager = assetsFiled .get (resources );
286- if (assetManager == appAssetManager ) {
287- assetsFiled .set (resources , newAssetManager );
288- }
267+ assetsFiled .set (resources , newAssetManager );
268+ } catch (Throwable ignore ) {
269+ // N
270+ final Object resourceImpl = resourcesImplFiled .get (resources );
271+ // for Huawei HwResourcesImpl
272+ final Field implAssets = findField (resourceImpl , "mAssets" );
273+ implAssets .set (resourceImpl , newAssetManager );
289274 }
290275
291276 clearPreloadTypedArrayIssue (resources );
277+
292278 resources .updateConfiguration (resources .getConfiguration (), resources .getDisplayMetrics ());
293279 }
294280
295281 try {
296282 if (resourceImpls != null ) {
297283 for (WeakReference <Object > wr : resourceImpls .values ()) {
298- final Object resourcesImpl = wr .get ();
299- if (resourcesImpl != null ) {
300- final Object assetManager = implAssetsField .get (resourcesImpl );
301- if (assetManager == appAssetManager ) {
302- implAssetsField .set (resourcesImpl , newAssetManager );
303- }
284+ final Object resourceImpl = wr .get ();
285+ if (resourceImpl != null ) {
286+ final Field implAssets = findField (resourceImpl , "mAssets" );
287+ implAssets .set (resourceImpl , newAssetManager );
304288 }
305289 }
306290 }
307291 } catch (Throwable ignored ) {
308292 // Ignored.
309293 }
310294
311- if (isReInject ) {
312- ShareTinkerLog .i (TAG , "Re-injecting, skip rest logic." );
313- return ;
314- }
315-
316295 // Handle issues caused by WebView on Android N.
317296 // Issue: On Android N, if an activity contains a webview, when screen rotates
318297 // our resource patch may lost effects.
319298 // for 5.x/6.x, we found Couldn't expand RemoteView for StatusBarNotification Exception
320299 if (Build .VERSION .SDK_INT >= 24 ) {
321300 try {
322301 if (publicSourceDirField != null ) {
323- publicSourceDirField .set (app .getApplicationInfo (), externalResourceFile );
302+ publicSourceDirField .set (context .getApplicationInfo (), externalResourceFile );
324303 }
325304 } catch (Throwable ignore ) {
326305 // Ignored.
327306 }
328307 }
329308
330- if (!checkResUpdate (app )) {
309+ if (!checkResUpdate (context )) {
331310 throw new TinkerRuntimeException (ShareConstants .CHECK_RES_INSTALL_FAIL );
332311 }
333312
334- if (!PatchedResourcesInsuranceLogic .install (app , externalResourceFile )) {
313+ if (!PatchedResourcesInsuranceLogic .install (context , externalResourceFile )) {
335314 ShareTinkerLog .w (TAG , "tryLoadPatchFiles:PatchedResourcesInsuranceLogic install fail." );
336315 throw new TinkerRuntimeException ("fail to install PatchedResourcesInsuranceLogic." );
337316 }
0 commit comments