Snap for 5240828 from cfb529fc644302dc244f67eb37b1f09179ade300 to pie-cts-release

Change-Id: I05da5b024a4c62b8519b98e4cb564aa0073a57f5
diff --git a/README.version b/README.version
index 92d3d64..7f1bbd0 100644
--- a/README.version
+++ b/README.version
@@ -12,3 +12,4 @@
         Allow to share classloader via dexmaker.share_classloader system property (I8c2490c3ec8e8582dc41c486f8f7a406bd635ebb)
 	Allow 'Q' until we can replace the version check with a number based check
         Mark mocks as trusted (needs upstreaming)
+	Minimal change to correctly guess data directory when running in secondary users
\ No newline at end of file
diff --git a/dexmaker-tests/src/androidTest/java/com/android/dx/AppDataDirGuesserTest.java b/dexmaker-tests/src/androidTest/java/com/android/dx/AppDataDirGuesserTest.java
index b1ff25f..a4a3407 100644
--- a/dexmaker-tests/src/androidTest/java/com/android/dx/AppDataDirGuesserTest.java
+++ b/dexmaker-tests/src/androidTest/java/com/android/dx/AppDataDirGuesserTest.java
@@ -17,6 +17,7 @@
 package com.android.dx;
 
 import android.os.Build;
+import android.os.Process;
 import org.junit.Test;
 
 import java.io.File;
@@ -49,6 +50,14 @@
     }
 
     @Test
+    public void testGuessCacheDir_ForSecondaryUser() {
+        guessCacheDirFor("/data/app/a.b.c.apk:/data/app/d.e.f.apk")
+                .withNonWriteable("/data/data/a.b.c", "/data/data/d.e.f")
+                .withProcessUid(1110009)
+                .shouldGive("/data/user/11/a.b.c/cache", "/data/user/11/d.e.f/cache");
+    }
+
+    @Test
     public void testGuessCacheDir_StripHyphenatedSuffixes() {
         guessCacheDirFor("/data/app/a.b.c-2.apk").shouldGive("/data/data/a.b.c/cache");
     }
@@ -121,14 +130,22 @@
         }
     }
 
+    @Test
+    public void testGetProcessUid() {
+        AppDataDirGuesser guesser = new AppDataDirGuesser();
+        assertTrue(guesser.getProcessUid() == Process.myUid());
+    }
+
     private interface TestCondition {
         TestCondition withNonWriteable(String... files);
+        TestCondition withProcessUid(Integer uid);
         void shouldGive(String... files);
     }
 
     private TestCondition guessCacheDirFor(final String path) {
         final Set<String> notWriteable = new HashSet<>();
         return new TestCondition() {
+            private Integer processUid;
             public void shouldGive(String... files) {
                 AppDataDirGuesser guesser = new AppDataDirGuesser() {
                     @Override
@@ -139,6 +156,10 @@
                     boolean fileOrDirExists(File file) {
                         return true;
                     }
+                    @Override
+                    Integer getProcessUid() {
+                        return processUid;
+                    }
                 };
                 File[] results = guesser.guessPath(path);
                 assertNotNull("Null results for " + path, results);
@@ -152,6 +173,12 @@
                 notWriteable.addAll(Arrays.asList(files));
                 return this;
             }
+
+            @Override
+            public TestCondition withProcessUid(Integer uid) {
+                processUid = uid;
+                return this;
+            }
         };
     }
 }
diff --git a/dexmaker/src/main/java/com/android/dx/AppDataDirGuesser.java b/dexmaker/src/main/java/com/android/dx/AppDataDirGuesser.java
index fa0ef69..cc1d803 100644
--- a/dexmaker/src/main/java/com/android/dx/AppDataDirGuesser.java
+++ b/dexmaker/src/main/java/com/android/dx/AppDataDirGuesser.java
@@ -18,6 +18,7 @@
 
 import java.io.File;
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -25,6 +26,9 @@
  * Uses heuristics to guess the application's private data directory.
  */
 class AppDataDirGuesser {
+    // Copied from UserHandle, indicates range of uids allocated for a user.
+    public static final int PER_USER_RANGE = 100000;
+
     public File guess() {
         try {
             ClassLoader classLoader = guessSuitableClassLoader();
@@ -146,8 +150,14 @@
                 end = dash;
             }
             String packageName = potential.substring(start, end);
-            File dataDir = new File("/data/data/" + packageName);
-            if (isWriteableDirectory(dataDir)) {
+            File dataDir = getWriteableDirectory("/data/data/" + packageName);
+
+            if (dataDir == null) {
+                // If we can't access "/data/data", try to guess user specific data directory.
+                dataDir = guessUserDataDirectory(packageName);
+            }
+
+            if (dataDir != null) {
                 File cacheDir = new File(dataDir, "cache");
                 // The cache directory might not exist -- create if necessary
                 if (fileOrDirExists(cacheDir) || cacheDir.mkdir()) {
@@ -179,4 +189,37 @@
     boolean isWriteableDirectory(File file) {
         return file.isDirectory() && file.canWrite();
     }
+
+    Integer getProcessUid() {
+        /* Uses reflection to try to fetch process UID. It will only work when executing on
+         * Android device. Otherwise, returns null.
+         */
+        try {
+            Method myUid = Class.forName("android.os.Process").getMethod("myUid");
+
+            // Invoke the method on a null instance, since it's a static method.
+            return (Integer) myUid.invoke(/* instance= */ null);
+        } catch (Exception e) {
+            // Catch any exceptions thrown and default to returning a null.
+            return null;
+        }
+    }
+
+    File guessUserDataDirectory(String packageName) {
+        Integer uid = getProcessUid();
+        if (uid == null) {
+            // If we couldn't retrieve process uid, return null.
+            return null;
+        }
+
+        // We're trying to get the ID of the Android user that's running the process. It can be
+        // inferred from the UID of the current process.
+        int userId = uid / PER_USER_RANGE;
+        return getWriteableDirectory(String.format("/data/user/%d/%s", userId, packageName));
+    }
+
+    private File getWriteableDirectory(String pathName) {
+        File dir = new File(pathName);
+        return isWriteableDirectory(dir) ? dir : null;
+    }
 }