ResourceRewriter.java revision 0657a5e0b853cb639b1b5a5a195125f9d6221a11
10657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba/* 20657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * Copyright (C) 2014 The Android Open Source Project 30657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * 40657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * Licensed under the Apache License, Version 2.0 (the "License"); 50657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * you may not use this file except in compliance with the License. 60657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * You may obtain a copy of the License at 70657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * 80657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * http://www.apache.org/licenses/LICENSE-2.0 90657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * 100657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * Unless required by applicable law or agreed to in writing, software 110657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * distributed under the License is distributed on an "AS IS" BASIS, 120657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * See the License for the specific language governing permissions and 140657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * limitations under the License. 150657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba */ 160657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 170657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosibapackage com.android.webview.chromium; 180657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 190657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosibaimport android.content.Context; 200657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosibaimport android.util.SparseArray; 210657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 220657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosibaimport java.lang.reflect.Field; 230657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosibaimport java.lang.reflect.Modifier; 240657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 250657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba/** 260657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * Helper class used to fix up resource ids. 270657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * This is mostly a copy of the code in frameworks/base/core/java/android/app/LoadedApk.java. 280657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * TODO: Remove if a cleaner mechanism is provided (either public API or AAPT is changed to generate 290657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba * this code). 300657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba */ 310657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosibaclass ResourceRewriter { 320657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 330657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba public static void rewriteRValues(Context ctx) { 340657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba // Rewrite the R 'constants' for all library apks. 350657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba SparseArray<String> packageIdentifiers = ctx.getResources().getAssets() 360657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba .getAssignedPackageIdentifiers(); 370657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba final int N = packageIdentifiers.size(); 380657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba for (int i = 0; i < N; i++) { 390657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba final int id = packageIdentifiers.keyAt(i); 400657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba final String name = packageIdentifiers.valueAt(i); 410657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba if (id == 0x01 || id == 0x7f) { 420657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba continue; 430657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 440657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 450657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba // TODO(mkosiba): We should use jarjar to remove the redundant R classes here, but due 460657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba // to a bug in jarjar it's not possible to rename classes with '$' in their name. 470657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba // See b/15684775. 480657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba rewriteRValues(com.android.webview.chromium.R.class, id); 490657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba rewriteRValues(org.chromium.content.R.class, id); 500657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba rewriteRValues(org.chromium.ui.R.class, id); 510657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 520657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba break; 530657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 540657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 550657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 560657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 570657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba private static void rewriteIntField(Field field, int packageId) throws IllegalAccessException { 580657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba int requiredModifiers = Modifier.STATIC | Modifier.PUBLIC; 590657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba int bannedModifiers = Modifier.FINAL; 600657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 610657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba int mod = field.getModifiers(); 620657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba if ((mod & requiredModifiers) != requiredModifiers || 630657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba (mod & bannedModifiers) != 0) { 640657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba throw new IllegalArgumentException("Field " + field.getName() + 650657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba " is not rewritable"); 660657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 670657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 680657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba if (field.getType() != int.class && field.getType() != Integer.class) { 690657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba throw new IllegalArgumentException("Field " + field.getName() + 700657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba " is not an integer"); 710657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 720657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 730657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba try { 740657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba int resId = field.getInt(null); 750657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba int newId = (resId & 0x00ffffff) | (packageId << 24); 760657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba field.setInt(null, newId); 770657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } catch (IllegalAccessException e) { 780657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba // This should not occur (we check above if we can write to it) 790657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba throw new IllegalArgumentException(e); 800657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 810657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 820657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 830657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba private static void rewriteIntArrayField(Field field, int packageId) { 840657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba int requiredModifiers = Modifier.STATIC | Modifier.PUBLIC; 850657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 860657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba if ((field.getModifiers() & requiredModifiers) != requiredModifiers) { 870657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba throw new IllegalArgumentException("Field " + field.getName() + 880657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba " is not rewritable"); 890657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 900657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 910657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba if (field.getType() != int[].class) { 920657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba throw new IllegalArgumentException("Field " + field.getName() + 930657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba " is not an integer array"); 940657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 950657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 960657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba try { 970657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba int[] array = (int[]) field.get(null); 980657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba for (int i = 0; i < array.length; i++) { 990657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba array[i] = (array[i] & 0x00ffffff) | (packageId << 24); 1000657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 1010657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } catch (IllegalAccessException e) { 1020657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba // This should not occur (we check above if we can write to it) 1030657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba throw new IllegalArgumentException(e); 1040657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 1050657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 1060657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 1070657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba private static void rewriteRValues(final Class<?> rClazz, int id) { 1080657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba try { 1090657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba Class<?>[] declaredClasses = rClazz.getDeclaredClasses(); 1100657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba for (Class<?> clazz : declaredClasses) { 1110657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba try { 1120657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba if (clazz.getSimpleName().equals("styleable")) { 1130657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba for (Field field : clazz.getDeclaredFields()) { 1140657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba if (field.getType() == int[].class) { 1150657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba rewriteIntArrayField(field, id); 1160657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 1170657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 1180657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 1190657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } else { 1200657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba for (Field field : clazz.getDeclaredFields()) { 1210657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba rewriteIntField(field, id); 1220657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 1230657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 1240657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } catch (Exception e) { 1250657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba throw new IllegalArgumentException("Failed to rewrite R values for " + 1260657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba clazz.getName(), e); 1270657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 1280657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 1290657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 1300657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } catch (Exception e) { 1310657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba throw new IllegalArgumentException("Failed to rewrite R values", e); 1320657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 1330657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba } 1340657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba 1350657a5e0b853cb639b1b5a5a195125f9d6221a11Marcin Kosiba} 136