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