• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.google.testing.littlemock;
18 
19 import java.io.File;
20 import java.lang.reflect.Field;
21 import java.util.ArrayList;
22 import java.util.List;
23 
24 /**
25  * Utility class for helping guess the application data directory.
26  */
27 public class AppDataDirGuesser {
28 
29   /** A single default instance of app data dir guesser, for overriding if you really need to. */
30   private static volatile AppDataDirGuesser sInjectableInstance = new AppDataDirGuesser();
31 
setInstance(AppDataDirGuesser instance)32   public static void setInstance(AppDataDirGuesser instance) {
33     sInjectableInstance = instance;
34   }
35 
getsInstance()36   public static AppDataDirGuesser getsInstance() {
37     return sInjectableInstance;
38   }
39 
guessSuitableDirectoryForGeneratedClasses()40   public File guessSuitableDirectoryForGeneratedClasses() {
41     try {
42       ClassLoader classLoader = AppDataDirGuesser.class.getClassLoader();
43       // Check that we have an instance of the PathClassLoader.
44       Class<?> clazz = Class.forName("dalvik.system.PathClassLoader");
45       clazz.cast(classLoader);
46       // Use the toString() method to calculate the data directory.
47       String pathFromThisClassLoader =
48           getPathFromPathClassLoader(classLoader, clazz);
49       File[] results = guessPath(pathFromThisClassLoader);
50       if (results.length > 0) {
51         return results[0];
52       }
53     } catch (ClassCastException e) {
54       // Fall through, we will return null.
55     } catch (ClassNotFoundException e) {
56       // Fall through, we will return null.
57     }
58     return null;
59   }
60 
getPathFromPathClassLoader( ClassLoader classLoader, Class<?> pathClassLoaderClass)61   private String getPathFromPathClassLoader(
62       ClassLoader classLoader, Class<?> pathClassLoaderClass) {
63     // Prior to ICS, we can simply read the "path" field of the
64     // PathClassLoader.
65     try {
66       Field pathField = pathClassLoaderClass.getDeclaredField("path");
67       pathField.setAccessible(true);
68       return (String) pathField.get(classLoader);
69     } catch (NoSuchFieldException e) {
70       // Ignore and fall back on parsing the output of toString below
71     } catch (IllegalAccessException e) {
72       // Ignore and fall back on parsing the output of toString below
73     } catch (ClassCastException e) {
74       // Ignore and fall back on parsing the output of toString below
75     }
76 
77     // Parsing toString() method: yuck.  But no other way to get the path.
78     // Strip out the bit between square brackets, that's our path.
79     String result = classLoader.toString();
80     int index = result.lastIndexOf('[');
81     result = (index == -1) ? result : result.substring(index + 1);
82     index = result.indexOf(']');
83     return (index == -1) ? result : result.substring(0, index);
84   }
85 
86   // @VisibleForTesting
guessPath(String input)87   File[] guessPath(String input) {
88     List<File> results = new ArrayList<File>();
89     for (String potential : splitPathList(input)) {
90       if (!potential.startsWith("/data/app/")) {
91         continue;
92       }
93       int start = "/data/app/".length();
94       int end = potential.lastIndexOf(".apk");
95       if (end != potential.length() - 4) {
96         continue;
97       }
98       int dash = potential.indexOf("-");
99       if (dash != -1) {
100         end = dash;
101       }
102       String packageName = potential.substring(start, end);
103       File dataDir = new File("/data/data/" + packageName);
104       if (isWriteableDirectory(dataDir)) {
105         File cacheDir = new File(dataDir, "cache");
106         if (fileOrDirExists(cacheDir) || makeDirectory(cacheDir)) {
107           if (isWriteableDirectory(cacheDir)) {
108             results.add(cacheDir);
109           }
110         }
111       }
112     }
113 
114     return results.toArray(new File[results.size()]);
115   }
116 
117   // @VisibleForTesting
splitPathList(String input)118   static String[] splitPathList(String input) {
119     String trimmed = input;
120     if (input.startsWith("dexPath=")) {
121       int start = "dexPath=".length();
122       int end = input.indexOf(',');
123 
124       trimmed = (end == -1) ? input.substring(start) :
125           input.substring(start, end);
126     }
127 
128     return trimmed.split(":");
129   }
130 
131   // @VisibleForTesting
fileOrDirExists(File file)132   boolean fileOrDirExists(File file) {
133       return file.exists();
134   }
135 
136   // @VisibleForTesting
makeDirectory(File file)137   boolean makeDirectory(File file) {
138       return file.mkdir();
139   }
140 
141   // @VisibleForTesting
isWriteableDirectory(File file)142   boolean isWriteableDirectory(File file) {
143     return file.isDirectory() && file.canWrite();
144   }
145 }
146