diff options
Diffstat (limited to 'core/java')
-rw-r--r-- | core/java/android/content/pm/PackageInstaller.java | 72 | ||||
-rw-r--r-- | core/java/android/os/FileBridge.java | 18 | ||||
-rw-r--r-- | core/java/com/android/internal/content/PackageHelper.java | 5 | ||||
-rw-r--r-- | core/java/com/android/internal/util/XmlUtils.java | 10 |
4 files changed, 89 insertions, 16 deletions
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 0a211cf..06d4c4a 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -285,6 +285,9 @@ public class PackageInstaller { * * @throws IOException if parameters were unsatisfiable, such as lack of * disk space or unavailable media. + * @throws SecurityException when installation services are unavailable, + * such as when called from a restricted user. + * @throws IllegalArgumentException when {@link SessionParams} is invalid. * @return positive, non-zero unique ID that represents the created session. * This ID remains consistent across device reboots until the * session is finalized. IDs are not reused during a given boot. @@ -303,6 +306,11 @@ public class PackageInstaller { /** * Open an existing session to actively perform work. To succeed, the caller * must be the owner of the install session. + * + * @throws IOException if parameters were unsatisfiable, such as lack of + * disk space or unavailable media. + * @throws SecurityException when the caller does not own the session, or + * the session is invalid. */ public @NonNull Session openSession(int sessionId) throws IOException { try { @@ -319,6 +327,9 @@ public class PackageInstaller { * Update the icon representing the app being installed in a specific * session. This should be roughly * {@link ActivityManager#getLauncherLargeIconSize()} in both dimensions. + * + * @throws SecurityException when the caller does not own the session, or + * the session is invalid. */ public void updateSessionAppIcon(int sessionId, @Nullable Bitmap appIcon) { try { @@ -331,6 +342,9 @@ public class PackageInstaller { /** * Update the label representing the app being installed in a specific * session. + * + * @throws SecurityException when the caller does not own the session, or + * the session is invalid. */ public void updateSessionAppLabel(int sessionId, @Nullable CharSequence appLabel) { try { @@ -341,6 +355,15 @@ public class PackageInstaller { } } + /** + * Completely abandon the given session, destroying all staged data and + * rendering it invalid. Abandoned sessions will be reported to + * {@link SessionCallback} listeners as failures. This is equivalent to + * opening the session and calling {@link Session#abandon()}. + * + * @throws SecurityException when the caller does not own the session, or + * the session is invalid. + */ public void abandonSession(int sessionId) { try { mInstaller.abandonSession(sessionId); @@ -350,7 +373,11 @@ public class PackageInstaller { } /** - * Return details for a specific session. + * Return details for a specific session. No special permissions are + * required to retrieve these details. + * + * @return details for the requested session, or {@code null} if the session + * does not exist. */ public @Nullable SessionInfo getSessionInfo(int sessionId) { try { @@ -361,7 +388,7 @@ public class PackageInstaller { } /** - * Return list of all active install sessions, regardless of the installer. + * Return list of all known install sessions, regardless of the installer. */ public @NonNull List<SessionInfo> getAllSessions() { final ApplicationInfo info = mContext.getApplicationInfo(); @@ -379,7 +406,7 @@ public class PackageInstaller { } /** - * Return list of all install sessions owned by the calling app. + * Return list of all known install sessions owned by the calling app. */ public @NonNull List<SessionInfo> getMySessions() { try { @@ -547,7 +574,8 @@ public class PackageInstaller { } /** - * Register to watch for session lifecycle events. + * Register to watch for session lifecycle events. No special permissions + * are required to watch for these events. */ public void registerSessionCallback(@NonNull SessionCallback callback) { registerSessionCallback(callback, new Handler()); @@ -560,7 +588,8 @@ public class PackageInstaller { } /** - * Register to watch for session lifecycle events. + * Register to watch for session lifecycle events. No special permissions + * are required to watch for these events. * * @param handler to dispatch callback events through, otherwise uses * calling thread. @@ -593,7 +622,7 @@ public class PackageInstaller { } /** - * Unregister an existing callback. + * Unregister a previously registered callback. */ public void unregisterSessionCallback(@NonNull SessionCallback callback) { synchronized (mDelegates) { @@ -686,6 +715,12 @@ public class PackageInstaller { * start at the beginning of the file. * @param lengthBytes total size of the file being written, used to * preallocate the underlying disk space, or -1 if unknown. + * The system may clear various caches as needed to allocate + * this space. + * @throws IOException if trouble opening the file for writing, such as + * lack of disk space or unavailable media. + * @throws SecurityException if called after the session has been + * committed or abandoned. */ public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes, long lengthBytes) throws IOException { @@ -719,6 +754,9 @@ public class PackageInstaller { * <p> * This returns all names which have been previously written through * {@link #openWrite(String, long, long)} as part of this session. + * + * @throws SecurityException if called after the session has been + * committed or abandoned. */ public @NonNull String[] getNames() throws IOException { try { @@ -738,6 +776,9 @@ public class PackageInstaller { * through {@link #openWrite(String, long, long)} as part of this * session. For example, this stream may be used to calculate a * {@link MessageDigest} of a written APK before committing. + * + * @throws SecurityException if called after the session has been + * committed or abandoned. */ public @NonNull InputStream openRead(@NonNull String name) throws IOException { try { @@ -759,6 +800,9 @@ public class PackageInstaller { * Once this method is called, no additional mutations may be performed * on the session. If the device reboots before the session has been * finalized, you may commit the session again. + * + * @throws SecurityException if streams opened through + * {@link #openWrite(String, long, long)} are still open. */ public void commit(@NonNull IntentSender statusReceiver) { try { @@ -783,7 +827,9 @@ public class PackageInstaller { /** * Completely abandon this session, destroying all staged data and - * rendering it invalid. + * rendering it invalid. Abandoned sessions will be reported to + * {@link SessionCallback} listeners as failures. This is equivalent to + * opening the session and calling {@link Session#abandon()}. */ public void abandon() { try { @@ -937,6 +983,18 @@ public class PackageInstaller { } /** {@hide} */ + public void setInstallFlagsInternal() { + installFlags |= PackageManager.INSTALL_INTERNAL; + installFlags &= ~PackageManager.INSTALL_EXTERNAL; + } + + /** {@hide} */ + public void setInstallFlagsExternal() { + installFlags |= PackageManager.INSTALL_EXTERNAL; + installFlags &= ~PackageManager.INSTALL_INTERNAL; + } + + /** {@hide} */ public void dump(IndentingPrintWriter pw) { pw.printPair("mode", mode); pw.printHexPair("installFlags", installFlags); diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java index 022a106..0acf24b 100644 --- a/core/java/android/os/FileBridge.java +++ b/core/java/android/os/FileBridge.java @@ -75,6 +75,13 @@ public class FileBridge extends Thread { return mClosed; } + public void forceClose() { + IoUtils.closeQuietly(mTarget); + IoUtils.closeQuietly(mServer); + IoUtils.closeQuietly(mClient); + mClosed = true; + } + public void setTargetFile(FileDescriptor target) { mTarget = target; } @@ -89,7 +96,6 @@ public class FileBridge extends Thread { try { while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) { final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN); - if (cmd == CMD_WRITE) { // Shuttle data into local file int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN); @@ -118,15 +124,10 @@ public class FileBridge extends Thread { } } - } catch (ErrnoException e) { - Log.wtf(TAG, "Failed during bridge", e); - } catch (IOException e) { + } catch (ErrnoException | IOException e) { Log.wtf(TAG, "Failed during bridge", e); } finally { - IoUtils.closeQuietly(mTarget); - IoUtils.closeQuietly(mServer); - IoUtils.closeQuietly(mClient); - mClosed = true; + forceClose(); } } @@ -151,6 +152,7 @@ public class FileBridge extends Thread { writeCommandAndBlock(CMD_CLOSE, "close()"); } finally { IoBridge.closeAndSignalBlockedThreads(mClient); + IoUtils.closeQuietly(mClientPfd); } } diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java index c17f4ee..7bdb4be 100644 --- a/core/java/com/android/internal/content/PackageHelper.java +++ b/core/java/com/android/internal/content/PackageHelper.java @@ -390,7 +390,10 @@ public class PackageHelper { if (!emulated && (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL)) { final File target = new UserEnvironment(UserHandle.USER_OWNER) .getExternalStorageDirectory(); - fitsOnExternal = (sizeBytes <= storage.getStorageBytesUntilLow(target)); + // External is only an option when size is known + if (sizeBytes > 0) { + fitsOnExternal = (sizeBytes <= storage.getStorageBytesUntilLow(target)); + } } if (prefer == RECOMMEND_INSTALL_INTERNAL) { diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java index 7db70ba..45d790b 100644 --- a/core/java/com/android/internal/util/XmlUtils.java +++ b/core/java/com/android/internal/util/XmlUtils.java @@ -1440,6 +1440,16 @@ public class XmlUtils { return Boolean.parseBoolean(value); } + public static boolean readBooleanAttribute(XmlPullParser in, String name, + boolean defaultValue) { + final String value = in.getAttributeValue(null, name); + if (value == null) { + return defaultValue; + } else { + return Boolean.parseBoolean(value); + } + } + public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value) throws IOException { out.attribute(null, name, Boolean.toString(value)); |