AppDataDirGuesser.java revision b09530734220ef8b19f8bfbe66659e92b0ddc795
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.google.dexmaker; 18 19import java.io.File; 20import java.lang.reflect.Field; 21import java.util.ArrayList; 22import java.util.List; 23 24/** 25 * Uses heuristics to guess the application's private data directory. 26 */ 27class AppDataDirGuesser { 28 public File guess() { 29 try { 30 ClassLoader classLoader = guessSuitableClassLoader(); 31 // Check that we have an instance of the PathClassLoader. 32 Class<?> clazz = Class.forName("dalvik.system.PathClassLoader"); 33 clazz.cast(classLoader); 34 // Use the toString() method to calculate the data directory. 35 String pathFromThisClassLoader = getPathFromThisClassLoader(classLoader, clazz); 36 File[] results = guessPath(pathFromThisClassLoader); 37 if (results.length > 0) { 38 return results[0]; 39 } 40 } catch (ClassCastException ignored) { 41 } catch (ClassNotFoundException ignored) { 42 } 43 return null; 44 } 45 46 private ClassLoader guessSuitableClassLoader() { 47 return AppDataDirGuesser.class.getClassLoader(); 48 } 49 50 private String getPathFromThisClassLoader(ClassLoader classLoader, 51 Class<?> pathClassLoaderClass) { 52 // Prior to ICS, we can simply read the "path" field of the 53 // PathClassLoader. 54 try { 55 Field pathField = pathClassLoaderClass.getDeclaredField("path"); 56 pathField.setAccessible(true); 57 return (String) pathField.get(classLoader); 58 } catch (NoSuchFieldException ignored) { 59 } catch (IllegalAccessException ignored) { 60 } catch (ClassCastException ignored) { 61 } 62 63 // Parsing toString() method: yuck. But no other way to get the path. 64 // Strip out the bit between angle brackets, that's our path. 65 String result = classLoader.toString(); 66 int index = result.lastIndexOf('['); 67 result = (index == -1) ? result : result.substring(index + 1); 68 index = result.indexOf(']'); 69 return (index == -1) ? result : result.substring(0, index); 70 } 71 72 File[] guessPath(String input) { 73 List<File> results = new ArrayList<File>(); 74 // Post JB, the system property is set to the applications private data cache 75 // directory. 76 File tmpDir = new File(System.getProperty("java.io.tmpdir")); 77 if (isWriteableDirectory(tmpDir)) { 78 results.add(tmpDir); 79 } 80 for (String potential : input.split(":")) { 81 if (!potential.startsWith("/data/app/")) { 82 continue; 83 } 84 int start = "/data/app/".length(); 85 int end = potential.lastIndexOf(".apk"); 86 if (end != potential.length() - 4) { 87 continue; 88 } 89 int dash = potential.indexOf("-"); 90 if (dash != -1) { 91 end = dash; 92 } 93 String packageName = potential.substring(start, end); 94 File dataDir = new File("/data/data/" + packageName); 95 if (isWriteableDirectory(dataDir)) { 96 File cacheDir = new File(dataDir, "cache"); 97 // The cache directory might not exist -- create if necessary 98 if (fileOrDirExists(cacheDir) || cacheDir.mkdir()) { 99 if (isWriteableDirectory(cacheDir)) { 100 results.add(cacheDir); 101 } 102 } 103 } 104 } 105 return results.toArray(new File[results.size()]); 106 } 107 108 boolean fileOrDirExists(File file) { 109 return file.exists(); 110 } 111 112 boolean isWriteableDirectory(File file) { 113 return file.isDirectory() && file.canWrite(); 114 } 115} 116