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