Final push of installd to Binder; goodbye socket!

Move last two straggling installd commands to Binder and destroy the
socket-based communication channel forever.

Test: builds, boots, apps install fine, pre-OTA dexopt works
Bug: 13758960, 30944031
Change-Id: I9afb9f71858febde34a94f53839b2986493b68a0
diff --git a/Android.mk b/Android.mk
index 1412155..2b565e5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -478,6 +478,7 @@
 
 LOCAL_SRC_FILES +=  \
 	../../system/netd/server/binder/android/net/INetd.aidl \
+	../native/cmds/installd/binder/android/os/IInstalld.aidl \
 
 LOCAL_AIDL_INCLUDES += system/update_engine/binder_bindings
 
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
deleted file mode 100644
index 62f674c..0000000
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.internal.os;
-
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
-import android.os.SystemClock;
-import android.text.TextUtils;
-import android.util.Slog;
-
-import com.android.internal.util.Preconditions;
-
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Arrays;
-
-/**
- * Represents a connection to {@code installd}. Allows multiple connect and
- * disconnect cycles.
- *
- * @hide for internal use only
- */
-public class InstallerConnection {
-    private static final String TAG = "InstallerConnection";
-    private static final boolean LOCAL_DEBUG = false;
-
-    private InputStream mIn;
-    private OutputStream mOut;
-    private LocalSocket mSocket;
-
-    private volatile Object mWarnIfHeld;
-
-    private final byte buf[] = new byte[1024];
-
-    public InstallerConnection() {
-    }
-
-    /**
-     * Yell loudly if someone tries making future calls while holding a lock on
-     * the given object.
-     */
-    public void setWarnIfHeld(Object warnIfHeld) {
-        Preconditions.checkState(mWarnIfHeld == null);
-        mWarnIfHeld = Preconditions.checkNotNull(warnIfHeld);
-    }
-
-    public synchronized String transact(String cmd) {
-        if (mWarnIfHeld != null && Thread.holdsLock(mWarnIfHeld)) {
-            Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding 0x"
-                    + Integer.toHexString(System.identityHashCode(mWarnIfHeld)), new Throwable());
-        }
-
-        if (!connect()) {
-            Slog.e(TAG, "connection failed");
-            return "-1";
-        }
-
-        if (!writeCommand(cmd)) {
-            /*
-             * If installd died and restarted in the background (unlikely but
-             * possible) we'll fail on the next write (this one). Try to
-             * reconnect and write the command one more time before giving up.
-             */
-            Slog.e(TAG, "write command failed? reconnect!");
-            if (!connect() || !writeCommand(cmd)) {
-                return "-1";
-            }
-        }
-        if (LOCAL_DEBUG) {
-            Slog.i(TAG, "send: '" + cmd + "'");
-        }
-
-        final int replyLength = readReply();
-        if (replyLength > 0) {
-            String s = new String(buf, 0, replyLength);
-            if (LOCAL_DEBUG) {
-                Slog.i(TAG, "recv: '" + s + "'");
-            }
-            return s;
-        } else {
-            if (LOCAL_DEBUG) {
-                Slog.i(TAG, "fail");
-            }
-            return "-1";
-        }
-    }
-
-    public String[] execute(String cmd, Object... args) throws InstallerException {
-        final StringBuilder builder = new StringBuilder(cmd);
-        for (Object arg : args) {
-            String escaped;
-            if (arg == null) {
-                escaped = "";
-            } else {
-                escaped = String.valueOf(arg);
-            }
-            if (escaped.indexOf('\0') != -1 || escaped.indexOf(' ') != -1 || "!".equals(escaped)) {
-                throw new InstallerException(
-                        "Invalid argument while executing " + cmd + " " + Arrays.toString(args));
-            }
-            if (TextUtils.isEmpty(escaped)) {
-                escaped = "!";
-            }
-            builder.append(' ').append(escaped);
-        }
-        final String[] resRaw = transact(builder.toString()).split(" ");
-        int res = -1;
-        try {
-            res = Integer.parseInt(resRaw[0]);
-        } catch (ArrayIndexOutOfBoundsException | NumberFormatException ignored) {
-        }
-        if (res != 0) {
-            throw new InstallerException(
-                    "Failed to execute " + cmd + " " + Arrays.toString(args) + ": " + res);
-        }
-        return resRaw;
-    }
-
-    public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
-            int dexoptNeeded, String outputPath, int dexFlags, String compilerFilter,
-            String volumeUuid, String sharedLibraries) throws InstallerException {
-        execute("dexopt",
-                apkPath,
-                uid,
-                pkgName,
-                instructionSet,
-                dexoptNeeded,
-                outputPath,
-                dexFlags,
-                compilerFilter,
-                volumeUuid,
-                sharedLibraries);
-    }
-
-    private boolean safeParseBooleanResult(String[] res) throws InstallerException {
-        if ((res == null) || (res.length != 2)) {
-            throw new InstallerException("Invalid size result: " + Arrays.toString(res));
-        }
-
-        // Just as a sanity check. Anything != "true" will be interpreted as false by parseBoolean.
-        if (!res[1].equals("true") && !res[1].equals("false")) {
-            throw new InstallerException("Invalid boolean result: " + Arrays.toString(res));
-        }
-
-        return Boolean.parseBoolean(res[1]);
-    }
-
-    public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
-        final String[] res = execute("merge_profiles", uid, pkgName);
-
-        return safeParseBooleanResult(res);
-    }
-
-    public boolean dumpProfiles(String gid, String packageName, String codePaths)
-            throws InstallerException {
-        final String[] res = execute("dump_profiles", gid, packageName, codePaths);
-
-        return safeParseBooleanResult(res);
-    }
-
-    private boolean connect() {
-        if (mSocket != null) {
-            return true;
-        }
-        Slog.i(TAG, "connecting...");
-        try {
-            mSocket = new LocalSocket();
-
-            LocalSocketAddress address = new LocalSocketAddress("installd",
-                    LocalSocketAddress.Namespace.RESERVED);
-
-            mSocket.connect(address);
-
-            mIn = mSocket.getInputStream();
-            mOut = mSocket.getOutputStream();
-        } catch (IOException ex) {
-            disconnect();
-            return false;
-        }
-        return true;
-    }
-
-    public void disconnect() {
-        Slog.i(TAG, "disconnecting...");
-        IoUtils.closeQuietly(mSocket);
-        IoUtils.closeQuietly(mIn);
-        IoUtils.closeQuietly(mOut);
-
-        mSocket = null;
-        mIn = null;
-        mOut = null;
-    }
-
-
-    private boolean readFully(byte[] buffer, int len) {
-        try {
-            Streams.readFully(mIn, buffer, 0, len);
-        } catch (IOException ioe) {
-            Slog.e(TAG, "read exception");
-            disconnect();
-            return false;
-        }
-
-        if (LOCAL_DEBUG) {
-            Slog.i(TAG, "read " + len + " bytes");
-        }
-
-        return true;
-    }
-
-    private int readReply() {
-        if (!readFully(buf, 2)) {
-            return -1;
-        }
-
-        final int len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
-        if ((len < 1) || (len > buf.length)) {
-            Slog.e(TAG, "invalid reply length (" + len + ")");
-            disconnect();
-            return -1;
-        }
-
-        if (!readFully(buf, len)) {
-            return -1;
-        }
-
-        return len;
-    }
-
-    private boolean writeCommand(String cmdString) {
-        final byte[] cmd = cmdString.getBytes();
-        final int len = cmd.length;
-        if ((len < 1) || (len > buf.length)) {
-            return false;
-        }
-
-        buf[0] = (byte) (len & 0xff);
-        buf[1] = (byte) ((len >> 8) & 0xff);
-        try {
-            mOut.write(buf, 0, 2);
-            mOut.write(cmd, 0, len);
-        } catch (IOException ex) {
-            Slog.e(TAG, "write error");
-            disconnect();
-            return false;
-        }
-        return true;
-    }
-
-    public void waitForConnection() {
-        for (;;) {
-            try {
-                execute("ping");
-                return;
-            } catch (InstallerException ignored) {
-            }
-            Slog.w(TAG, "installd not ready");
-            SystemClock.sleep(1000);
-        }
-    }
-
-    public static class InstallerException extends Exception {
-        public InstallerException(String detailMessage) {
-            super(detailMessage);
-        }
-    }
-}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 5b46a09..ef5231c 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -26,7 +26,11 @@
 import android.icu.util.ULocale;
 import android.net.LocalServerSocket;
 import android.opengl.EGL14;
+import android.os.IInstalld;
 import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
@@ -42,7 +46,6 @@
 import android.webkit.WebViewFactory;
 import android.widget.TextView;
 
-import com.android.internal.os.InstallerConnection.InstallerException;
 
 import dalvik.system.DexFile;
 import dalvik.system.PathClassLoader;
@@ -493,59 +496,55 @@
      */
     private static void performSystemServerDexOpt(String classPath) {
         final String[] classPathElements = classPath.split(":");
-        final InstallerConnection installer = new InstallerConnection();
-        installer.waitForConnection();
+        final IInstalld installd = IInstalld.Stub
+                .asInterface(ServiceManager.getService("installd"));
         final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
 
-        try {
-            String sharedLibraries = "";
-            for (String classPathElement : classPathElements) {
-                // System server is fully AOTed and never profiled
-                // for profile guided compilation.
-                // TODO: Make this configurable between INTERPRET_ONLY, SPEED, SPACE and EVERYTHING?
+        String sharedLibraries = "";
+        for (String classPathElement : classPathElements) {
+            // System server is fully AOTed and never profiled
+            // for profile guided compilation.
+            // TODO: Make this configurable between INTERPRET_ONLY, SPEED, SPACE and EVERYTHING?
 
-                int dexoptNeeded;
-                try {
-                    dexoptNeeded = DexFile.getDexOptNeeded(
-                        classPathElement, instructionSet, "speed",
-                        false /* newProfile */);
-                } catch (FileNotFoundException ignored) {
-                    // Do not add to the classpath.
-                    Log.w(TAG, "Missing classpath element for system server: " + classPathElement);
-                    continue;
-                } catch (IOException e) {
-                    // Not fully clear what to do here as we don't know the cause of the
-                    // IO exception. Add to the classpath to be conservative, but don't
-                    // attempt to compile it.
-                    Log.w(TAG, "Error checking classpath element for system server: "
-                            + classPathElement, e);
-                    dexoptNeeded = DexFile.NO_DEXOPT_NEEDED;
-                }
-
-                if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
-                    final String packageName = "*";
-                    final String outputPath = null;
-                    final int dexFlags = 0;
-                    final String compilerFilter = "speed";
-                    final String uuid = StorageManager.UUID_PRIVATE_INTERNAL;
-                    try {
-                        installer.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
-                                instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
-                                uuid, sharedLibraries);
-                    } catch (InstallerException e) {
-                        // Ignore (but log), we need this on the classpath for fallback mode.
-                        Log.w(TAG, "Failed compiling classpath element for system server: "
-                                + classPathElement, e);
-                    }
-                }
-
-                if (!sharedLibraries.isEmpty()) {
-                    sharedLibraries += ":";
-                }
-                sharedLibraries += classPathElement;
+            int dexoptNeeded;
+            try {
+                dexoptNeeded = DexFile.getDexOptNeeded(
+                    classPathElement, instructionSet, "speed",
+                    false /* newProfile */);
+            } catch (FileNotFoundException ignored) {
+                // Do not add to the classpath.
+                Log.w(TAG, "Missing classpath element for system server: " + classPathElement);
+                continue;
+            } catch (IOException e) {
+                // Not fully clear what to do here as we don't know the cause of the
+                // IO exception. Add to the classpath to be conservative, but don't
+                // attempt to compile it.
+                Log.w(TAG, "Error checking classpath element for system server: "
+                        + classPathElement, e);
+                dexoptNeeded = DexFile.NO_DEXOPT_NEEDED;
             }
-        } finally {
-            installer.disconnect();
+
+            if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
+                final String packageName = "*";
+                final String outputPath = null;
+                final int dexFlags = 0;
+                final String compilerFilter = "speed";
+                final String uuid = StorageManager.UUID_PRIVATE_INTERNAL;
+                try {
+                    installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
+                            instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
+                            uuid, sharedLibraries);
+                } catch (RemoteException | ServiceSpecificException e) {
+                    // Ignore (but log), we need this on the classpath for fallback mode.
+                    Log.w(TAG, "Failed compiling classpath element for system server: "
+                            + classPathElement, e);
+                }
+            }
+
+            if (!sharedLibraries.isEmpty()) {
+                sharedLibraries += ":";
+            }
+            sharedLibraries += classPathElement;
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4cf1ef5..f6ce503 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -35,7 +35,6 @@
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.os.Zygote;
-import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
@@ -54,6 +53,7 @@
 import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.pm.Installer;
+import com.android.server.pm.Installer.InstallerException;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.vr.VrManagerInternal;
 import com.android.server.wm.WindowManagerService;
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index f00065f..f144761 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -26,14 +26,10 @@
 import android.os.ServiceSpecificException;
 import android.util.Slog;
 
-import com.android.internal.os.InstallerConnection;
-import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.server.SystemService;
 
 import dalvik.system.VMRuntime;
 
-import java.util.Arrays;
-
 public class Installer extends SystemService {
     private static final String TAG = "Installer";
 
@@ -61,9 +57,7 @@
     private final boolean mIsolated;
 
     // TODO: reconnect if installd restarts
-    private final InstallerConnection mInstaller;
-    private final IInstalld mInstalld;
-
+    private volatile IInstalld mInstalld;
     private volatile Object mWarnIfHeld;
 
     public Installer(Context context) {
@@ -78,13 +72,6 @@
     public Installer(Context context, boolean isolated) {
         super(context);
         mIsolated = isolated;
-        if (isolated) {
-            mInstaller = null;
-            mInstalld = null;
-        } else {
-            mInstaller = new InstallerConnection();
-            mInstalld = IInstalld.Stub.asInterface(ServiceManager.getService("installd"));
-        }
     }
 
     /**
@@ -92,17 +79,15 @@
      * the given object.
      */
     public void setWarnIfHeld(Object warnIfHeld) {
-        if (mInstaller != null) {
-            mInstaller.setWarnIfHeld(warnIfHeld);
-        }
         mWarnIfHeld = warnIfHeld;
     }
 
     @Override
     public void onStart() {
-        if (mInstaller != null) {
-            Slog.i(TAG, "Waiting for installd to be ready.");
-            mInstaller.waitForConnection();
+        if (mIsolated) {
+            mInstalld = null;
+        } else {
+            mInstalld = IInstalld.Stub.asInterface(ServiceManager.getService("installd"));
         }
     }
 
@@ -131,7 +116,7 @@
             mInstalld.createAppData(uuid, packageName, userId, flags, appId, seInfo,
                     targetSdkVersion);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -141,7 +126,7 @@
         try {
             mInstalld.restoreconAppData(uuid, packageName, userId, flags, appId, seInfo);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -151,7 +136,7 @@
         try {
             mInstalld.migrateAppData(uuid, packageName, userId, flags);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -161,7 +146,7 @@
         try {
             mInstalld.clearAppData(uuid, packageName, userId, flags, ceDataInode);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -171,7 +156,7 @@
         try {
             mInstalld.destroyAppData(uuid, packageName, userId, flags, ceDataInode);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -183,21 +168,21 @@
             mInstalld.moveCompleteApp(fromUuid, toUuid, packageName, dataAppName, appId, seInfo,
                     targetSdkVersion);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
-    public void getAppSize(String uuid, String pkgname, int userid, int flags, long ceDataInode,
+    public void getAppSize(String uuid, String packageName, int userId, int flags, long ceDataInode,
             String codePath, PackageStats stats) throws InstallerException {
         if (!checkBeforeRemote()) return;
-        final String[] res = mInstaller.execute("get_app_size", uuid, pkgname, userid, flags,
-                ceDataInode, codePath);
         try {
-            stats.codeSize += Long.parseLong(res[1]);
-            stats.dataSize += Long.parseLong(res[2]);
-            stats.cacheSize += Long.parseLong(res[3]);
-        } catch (ArrayIndexOutOfBoundsException | NumberFormatException e) {
-            throw new InstallerException("Invalid size result: " + Arrays.toString(res));
+            final long[] res = mInstalld.getAppSize(uuid, packageName, userId, flags, ceDataInode,
+                    codePath);
+            stats.codeSize += res[0];
+            stats.dataSize += res[1];
+            stats.cacheSize += res[2];
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
         }
     }
 
@@ -207,7 +192,7 @@
         try {
             return mInstalld.getAppDataInode(uuid, packageName, userId, flags);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -217,8 +202,12 @@
             throws InstallerException {
         assertValidInstructionSet(instructionSet);
         if (!checkBeforeRemote()) return;
-        mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded,
-                outputPath, dexFlags, compilerFilter, volumeUuid, sharedLibraries);
+        try {
+            mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
+                    dexFlags, compilerFilter, volumeUuid, sharedLibraries);
+        } catch (RemoteException | ServiceSpecificException e) {
+            throw InstallerException.from(e);
+        }
     }
 
     public boolean mergeProfiles(int uid, String packageName) throws InstallerException {
@@ -226,7 +215,7 @@
         try {
             return mInstalld.mergeProfiles(uid, packageName);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -236,7 +225,7 @@
         try {
             return mInstalld.dumpProfiles(uid, packageName, codePaths);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -246,7 +235,7 @@
         try {
             mInstalld.idmap(targetApkPath, overlayApkPath, uid);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -256,7 +245,7 @@
         try {
             mInstalld.rmdex(codePath, instructionSet);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -265,7 +254,7 @@
         try {
             mInstalld.rmPackageDir(packageDir);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -274,7 +263,7 @@
         try {
             mInstalld.clearAppProfiles(packageName);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -283,7 +272,7 @@
         try {
             mInstalld.destroyAppProfiles(packageName);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -293,7 +282,7 @@
         try {
             mInstalld.createUserData(uuid, userId, userSerial, flags);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -302,7 +291,7 @@
         try {
             mInstalld.destroyUserData(uuid, userId, flags);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -312,7 +301,7 @@
         try {
             mInstalld.markBootComplete(instructionSet);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -321,7 +310,7 @@
         try {
             mInstalld.freeCache(uuid, freeStorageSize);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -336,7 +325,7 @@
         try {
             mInstalld.linkNativeLibraryDirectory(uuid, packageName, nativeLibPath32, userId);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -346,7 +335,7 @@
         try {
             mInstalld.createOatDir(oatDir, dexInstructionSet);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -356,7 +345,7 @@
         try {
             mInstalld.linkFile(relativePath, fromBase, toBase);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -366,7 +355,7 @@
         try {
             mInstalld.moveAb(apkPath, instructionSet, outputPath);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -376,7 +365,7 @@
         try {
             mInstalld.deleteOdex(apkPath, instructionSet, outputPath);
         } catch (RemoteException | ServiceSpecificException e) {
-            throw new InstallerException(e.getMessage());
+            throw InstallerException.from(e);
         }
     }
 
@@ -389,4 +378,14 @@
         }
         throw new InstallerException("Invalid instruction set: " + instructionSet);
     }
+
+    public static class InstallerException extends Exception {
+        public InstallerException(String detailMessage) {
+            super(detailMessage);
+        }
+
+        public static InstallerException from(Exception e) throws InstallerException {
+            throw new InstallerException(e.getMessage());
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index c0ae272..7b078ca1 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -35,7 +35,7 @@
 import android.util.Slog;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.os.InstallerConnection.InstallerException;
+import com.android.server.pm.Installer.InstallerException;
 
 import java.io.File;
 import java.io.FileDescriptor;
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index acdcc72..30ff32b 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -27,8 +27,8 @@
 import android.util.Log;
 import android.util.Slog;
 
-import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.pm.Installer.InstallerException;
 
 import java.io.File;
 import java.io.IOException;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 2ece99f..c85e1d8 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -72,10 +72,10 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.PackageHelper;
-import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
+import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
 
 import java.io.File;
diff --git a/services/core/java/com/android/server/pm/PackageManagerException.java b/services/core/java/com/android/server/pm/PackageManagerException.java
index f243e63..0e3f173 100644
--- a/services/core/java/com/android/server/pm/PackageManagerException.java
+++ b/services/core/java/com/android/server/pm/PackageManagerException.java
@@ -19,7 +19,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser.PackageParserException;
 
-import com.android.internal.os.InstallerConnection.InstallerException;
+import com.android.server.pm.Installer.InstallerException;
 
 /** {@hide} */
 public class PackageManagerException extends Exception {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 81c31b8..87eafd7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -231,7 +231,6 @@
 import com.android.internal.content.PackageHelper;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.os.IParcelFileDescriptorFactory;
-import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.os.Zygote;
 import com.android.internal.telephony.CarrierAppUtils;
@@ -250,6 +249,7 @@
 import com.android.server.SystemConfig;
 import com.android.server.Watchdog;
 import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.PermissionsState.PermissionState;
 import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.pm.Settings.VersionInfo;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 335af08..39cae37 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -77,13 +77,13 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
-import com.android.internal.os.InstallerConnection.InstallerException;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.XmlUtils;
 import com.android.server.backup.PreferredActivityBackupHelper;
+import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.PackageManagerService.DumpState;
 import com.android.server.pm.PermissionsState.PermissionState;