173cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson/*
273cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * Copyright (C) 2012 The Android Open Source Project
373cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson *
473cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
573cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * you may not use this file except in compliance with the License.
673cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * You may obtain a copy of the License at
773cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson *
873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson *      http://www.apache.org/licenses/LICENSE-2.0
973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson *
1073cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * Unless required by applicable law or agreed to in writing, software
1173cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
1273cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1373cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * See the License for the specific language governing permissions and
1473cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * limitations under the License.
1573cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson */
1673cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson
1773cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilsonpackage com.google.dexmaker;
1873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson
1973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilsonimport java.io.File;
20ebb25ef16a03f478946593d2d2ada043e0007220Alex Klyubinimport java.lang.reflect.Field;
2173cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilsonimport java.util.ArrayList;
2273cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilsonimport java.util.List;
2373cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson
2473cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson/**
2573cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson * Uses heuristics to guess the application's private data directory.
2673cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson */
2773cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilsonclass AppDataDirGuesser {
2873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    public File guess() {
2973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        try {
3073cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            ClassLoader classLoader = guessSuitableClassLoader();
3173cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            // Check that we have an instance of the PathClassLoader.
3273cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            Class<?> clazz = Class.forName("dalvik.system.PathClassLoader");
3373cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            clazz.cast(classLoader);
3473cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            // Use the toString() method to calculate the data directory.
35ebb25ef16a03f478946593d2d2ada043e0007220Alex Klyubin            String pathFromThisClassLoader = getPathFromThisClassLoader(classLoader, clazz);
3673cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            File[] results = guessPath(pathFromThisClassLoader);
3773cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            if (results.length > 0) {
3873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson                return results[0];
3973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            }
4073cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        } catch (ClassCastException ignored) {
4173cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        } catch (ClassNotFoundException ignored) {
4273cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        }
4373cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        return null;
4473cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    }
4573cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson
4673cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    private ClassLoader guessSuitableClassLoader() {
4773cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        return AppDataDirGuesser.class.getClassLoader();
4873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    }
4973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson
50ebb25ef16a03f478946593d2d2ada043e0007220Alex Klyubin    private String getPathFromThisClassLoader(ClassLoader classLoader,
51ebb25ef16a03f478946593d2d2ada043e0007220Alex Klyubin            Class<?> pathClassLoaderClass) {
52ebb25ef16a03f478946593d2d2ada043e0007220Alex Klyubin        // Prior to ICS, we can simply read the "path" field of the
53ebb25ef16a03f478946593d2d2ada043e0007220Alex Klyubin        // PathClassLoader.
54ebb25ef16a03f478946593d2d2ada043e0007220Alex Klyubin        try {
55ebb25ef16a03f478946593d2d2ada043e0007220Alex Klyubin            Field pathField = pathClassLoaderClass.getDeclaredField("path");
56ebb25ef16a03f478946593d2d2ada043e0007220Alex Klyubin            pathField.setAccessible(true);
57ebb25ef16a03f478946593d2d2ada043e0007220Alex Klyubin            return (String) pathField.get(classLoader);
58ebb25ef16a03f478946593d2d2ada043e0007220Alex Klyubin        } catch (NoSuchFieldException ignored) {
59ebb25ef16a03f478946593d2d2ada043e0007220Alex Klyubin        } catch (IllegalAccessException ignored) {
60ebb25ef16a03f478946593d2d2ada043e0007220Alex Klyubin        } catch (ClassCastException ignored) {
61ebb25ef16a03f478946593d2d2ada043e0007220Alex Klyubin        }
62ebb25ef16a03f478946593d2d2ada043e0007220Alex Klyubin
6373cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        // Parsing toString() method: yuck.  But no other way to get the path.
6495689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy        // Strip out the bit between square brackets, that's our path.
6573cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        String result = classLoader.toString();
6673cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        int index = result.lastIndexOf('[');
6773cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        result = (index == -1) ? result : result.substring(index + 1);
6873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        index = result.indexOf(']');
6973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        return (index == -1) ? result : result.substring(0, index);
7073cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    }
7173cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson
7273cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    File[] guessPath(String input) {
7373cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        List<File> results = new ArrayList<File>();
7495689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy        for (String potential : splitPathList(input)) {
7573cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            if (!potential.startsWith("/data/app/")) {
7673cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson                continue;
7773cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            }
7873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            int start = "/data/app/".length();
7973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            int end = potential.lastIndexOf(".apk");
8073cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            if (end != potential.length() - 4) {
8173cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson                continue;
8273cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            }
8373cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            int dash = potential.indexOf("-");
8473cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            if (dash != -1) {
8573cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson                end = dash;
8673cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            }
87524c023fb37b41e06b69f1b696100dd465acb353Alex Klyubin            String packageName = potential.substring(start, end);
88524c023fb37b41e06b69f1b696100dd465acb353Alex Klyubin            File dataDir = new File("/data/data/" + packageName);
89524c023fb37b41e06b69f1b696100dd465acb353Alex Klyubin            if (isWriteableDirectory(dataDir)) {
90524c023fb37b41e06b69f1b696100dd465acb353Alex Klyubin                File cacheDir = new File(dataDir, "cache");
91524c023fb37b41e06b69f1b696100dd465acb353Alex Klyubin                // The cache directory might not exist -- create if necessary
92524c023fb37b41e06b69f1b696100dd465acb353Alex Klyubin                if (fileOrDirExists(cacheDir) || cacheDir.mkdir()) {
93524c023fb37b41e06b69f1b696100dd465acb353Alex Klyubin                    if (isWriteableDirectory(cacheDir)) {
94524c023fb37b41e06b69f1b696100dd465acb353Alex Klyubin                        results.add(cacheDir);
95524c023fb37b41e06b69f1b696100dd465acb353Alex Klyubin                    }
96524c023fb37b41e06b69f1b696100dd465acb353Alex Klyubin                }
9773cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson            }
9873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        }
9973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        return results.toArray(new File[results.size()]);
10073cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    }
10173cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson
10295689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy    static String[] splitPathList(String input) {
10395689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy       String trimmed = input;
10495689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy       if (input.startsWith("dexPath=")) {
10595689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            int start = "dexPath=".length();
10695689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy            int end = input.indexOf(',');
10795689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy
10895689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy           trimmed = (end == -1) ? input.substring(start) : input.substring(start, end);
10995689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy       }
11095689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy
11195689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy       return trimmed.split(":");
11295689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy    }
11395689a700bfea5e2d78380a442fc2903cc40a3f2Mark Brophy
114524c023fb37b41e06b69f1b696100dd465acb353Alex Klyubin    boolean fileOrDirExists(File file) {
115524c023fb37b41e06b69f1b696100dd465acb353Alex Klyubin        return file.exists();
116524c023fb37b41e06b69f1b696100dd465acb353Alex Klyubin    }
117524c023fb37b41e06b69f1b696100dd465acb353Alex Klyubin
11873cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    boolean isWriteableDirectory(File file) {
11973cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson        return file.isDirectory() && file.canWrite();
12073cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson    }
12173cfa4498f640e0915b95fc806db4a0d54172fe8Jesse Wilson}
122