summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt16
-rw-r--r--core/java/android/app/Activity.java4
-rw-r--r--core/java/android/app/backup/BackupDataInput.java6
-rw-r--r--core/java/android/app/backup/BackupDataOutput.java3
-rw-r--r--core/java/android/app/backup/BackupTransport.java2
-rw-r--r--core/java/android/content/pm/PackageInstaller.java19
-rw-r--r--core/java/android/os/storage/StorageManager.java10
-rw-r--r--core/java/android/view/WindowCallbackWrapper.java144
-rw-r--r--core/java/android/widget/FastScroller.java30
-rw-r--r--core/java/com/android/internal/app/IMediaContainerService.aidl5
-rw-r--r--core/java/com/android/internal/app/ToolbarActionBar.java24
-rw-r--r--core/java/com/android/internal/content/PackageHelper.java83
-rw-r--r--core/res/res/drawable/fastscroll_label_left_material.xml27
-rw-r--r--core/res/res/drawable/fastscroll_label_right_material.xml27
-rw-r--r--core/res/res/values/attrs.xml3
-rw-r--r--core/res/res/values/config.xml7
-rw-r--r--core/res/res/values/styles_material.xml4
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/res/res/values/themes_material.xml12
-rw-r--r--graphics/java/android/graphics/drawable/AnimationDrawable.java4
-rw-r--r--graphics/java/android/graphics/drawable/InsetDrawable.java33
-rw-r--r--media/java/android/media/tv/TvInputInfo.java30
-rw-r--r--media/java/android/media/tv/TvInputManager.java34
-rw-r--r--media/java/android/media/tv/TvInputService.java3
-rw-r--r--packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java292
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java241
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java163
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java225
-rw-r--r--services/core/java/com/android/server/tv/TvInputHal.java11
-rw-r--r--services/core/java/com/android/server/tv/TvInputHardwareManager.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java11
-rw-r--r--tests/ActivityTests/AndroidManifest.xml1
-rw-r--r--tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java7
-rw-r--r--tools/aapt/Android.mk1
-rw-r--r--tools/aapt/Command.cpp2
35 files changed, 892 insertions, 598 deletions
diff --git a/api/current.txt b/api/current.txt
index f2200af..0154dd2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8666,14 +8666,14 @@ package android.content.pm {
field public static final java.lang.String EXTRA_STATUS = "android.content.pm.extra.STATUS";
field public static final java.lang.String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
field public static final int STATUS_FAILURE = 1; // 0x1
- field public static final int STATUS_FAILURE_ABORTED = 2; // 0x2
- field public static final int STATUS_FAILURE_BLOCKED = 1; // 0x1
- field public static final int STATUS_FAILURE_CONFLICT = 4; // 0x4
- field public static final int STATUS_FAILURE_INCOMPATIBLE = 6; // 0x6
- field public static final int STATUS_FAILURE_INVALID = 3; // 0x3
- field public static final int STATUS_FAILURE_STORAGE = 5; // 0x5
+ field public static final int STATUS_FAILURE_ABORTED = 3; // 0x3
+ field public static final int STATUS_FAILURE_BLOCKED = 2; // 0x2
+ field public static final int STATUS_FAILURE_CONFLICT = 5; // 0x5
+ field public static final int STATUS_FAILURE_INCOMPATIBLE = 7; // 0x7
+ field public static final int STATUS_FAILURE_INVALID = 4; // 0x4
+ field public static final int STATUS_FAILURE_STORAGE = 6; // 0x6
+ field public static final int STATUS_PENDING_USER_ACTION = -1; // 0xffffffff
field public static final int STATUS_SUCCESS = 0; // 0x0
- field public static final int STATUS_USER_ACTION_REQUIRED = -1; // 0xffffffff
}
public static class PackageInstaller.Session implements java.io.Closeable {
@@ -8681,7 +8681,7 @@ package android.content.pm {
method public void close();
method public void commit(android.content.IntentSender);
method public void fsync(java.io.OutputStream) throws java.io.IOException;
- method public java.lang.String[] getNames();
+ method public java.lang.String[] getNames() throws java.io.IOException;
method public java.io.InputStream openRead(java.lang.String) throws java.io.IOException;
method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
method public void setProgress(float);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e5bffee..f5ac5f7 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2095,7 +2095,9 @@ public class Activity extends ContextThemeWrapper
"by the window decor. Do not request Window.FEATURE_ACTION_BAR and set " +
"android:windowActionBar to false in your theme to use a Toolbar instead.");
}
- mActionBar = new ToolbarActionBar(toolbar, getTitle(), this);
+ ToolbarActionBar tbab = new ToolbarActionBar(toolbar, getTitle(), this);
+ mActionBar = tbab;
+ mWindow.setCallback(tbab.getWrappedWindowCallback());
mActionBar.invalidateOptionsMenu();
}
diff --git a/core/java/android/app/backup/BackupDataInput.java b/core/java/android/app/backup/BackupDataInput.java
index 03205fb..26f9e3e 100644
--- a/core/java/android/app/backup/BackupDataInput.java
+++ b/core/java/android/app/backup/BackupDataInput.java
@@ -16,6 +16,8 @@
package android.app.backup;
+import android.annotation.SystemApi;
+
import java.io.FileDescriptor;
import java.io.IOException;
@@ -70,6 +72,7 @@ public class BackupDataInput {
}
/** @hide */
+ @SystemApi
public BackupDataInput(FileDescriptor fd) {
if (fd == null) throw new NullPointerException();
mBackupReader = ctor(fd);
@@ -79,6 +82,7 @@ public class BackupDataInput {
}
/** @hide */
+ @Override
protected void finalize() throws Throwable {
try {
dtor(mBackupReader);
@@ -174,7 +178,7 @@ public class BackupDataInput {
* for further processing. This allows a {@link android.app.backup.BackupAgent} to
* efficiently discard obsolete or otherwise uninteresting records during the
* restore operation.
- *
+ *
* @throws IOException if an error occurred when trying to read the restore data stream
*/
public void skipEntityData() throws IOException {
diff --git a/core/java/android/app/backup/BackupDataOutput.java b/core/java/android/app/backup/BackupDataOutput.java
index fc5fb3d..048a4bb 100644
--- a/core/java/android/app/backup/BackupDataOutput.java
+++ b/core/java/android/app/backup/BackupDataOutput.java
@@ -16,6 +16,7 @@
package android.app.backup;
+import android.annotation.SystemApi;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -66,6 +67,7 @@ public class BackupDataOutput {
long mBackupWriter;
/** @hide */
+ @SystemApi
public BackupDataOutput(FileDescriptor fd) {
if (fd == null) throw new NullPointerException();
mBackupWriter = ctor(fd);
@@ -115,6 +117,7 @@ public class BackupDataOutput {
}
/** @hide */
+ @Override
protected void finalize() throws Throwable {
try {
dtor(mBackupWriter);
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index 6cfabf0..70bb5e4 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -51,7 +51,7 @@ public class BackupTransport {
public static final int AGENT_UNKNOWN = -1004;
IBackupTransport mBinderImpl = new TransportImpl();
- /** @hide */
+
public IBinder getBinder() {
return mBinderImpl.asBinder();
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index aa4ea45..4ac701f 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -129,7 +129,7 @@ public class PackageInstaller {
*
* @see Intent#getParcelableExtra(String)
*/
- public static final int STATUS_USER_ACTION_REQUIRED = -1;
+ public static final int STATUS_PENDING_USER_ACTION = -1;
/**
* The operation succeeded.
@@ -152,7 +152,7 @@ public class PackageInstaller {
*
* @see #EXTRA_STATUS_MESSAGE
*/
- public static final int STATUS_FAILURE_BLOCKED = 1;
+ public static final int STATUS_FAILURE_BLOCKED = 2;
/**
* The operation failed because it was actively aborted. For example, the
@@ -161,7 +161,7 @@ public class PackageInstaller {
*
* @see #EXTRA_STATUS_MESSAGE
*/
- public static final int STATUS_FAILURE_ABORTED = 2;
+ public static final int STATUS_FAILURE_ABORTED = 3;
/**
* The operation failed because one or more of the APKs was invalid. For
@@ -170,7 +170,7 @@ public class PackageInstaller {
*
* @see #EXTRA_STATUS_MESSAGE
*/
- public static final int STATUS_FAILURE_INVALID = 3;
+ public static final int STATUS_FAILURE_INVALID = 4;
/**
* The operation failed because it conflicts (or is inconsistent with) with
@@ -183,7 +183,7 @@ public class PackageInstaller {
*
* @see #EXTRA_STATUS_MESSAGE
*/
- public static final int STATUS_FAILURE_CONFLICT = 4;
+ public static final int STATUS_FAILURE_CONFLICT = 5;
/**
* The operation failed because of storage issues. For example, the device
@@ -192,7 +192,7 @@ public class PackageInstaller {
*
* @see #EXTRA_STATUS_MESSAGE
*/
- public static final int STATUS_FAILURE_STORAGE = 5;
+ public static final int STATUS_FAILURE_STORAGE = 6;
/**
* The operation failed because it is fundamentally incompatible with this
@@ -202,7 +202,7 @@ public class PackageInstaller {
*
* @see #EXTRA_STATUS_MESSAGE
*/
- public static final int STATUS_FAILURE_INCOMPATIBLE = 6;
+ public static final int STATUS_FAILURE_INCOMPATIBLE = 7;
private final Context mContext;
private final PackageManager mPm;
@@ -584,9 +584,12 @@ public class PackageInstaller {
* This returns all names which have been previously written through
* {@link #openWrite(String, long, long)} as part of this session.
*/
- public @NonNull String[] getNames() {
+ public @NonNull String[] getNames() throws IOException {
try {
return mSession.getNames();
+ } catch (RuntimeException e) {
+ ExceptionUtils.maybeUnwrapIOException(e);
+ throw e;
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 68b91cb..4cdafe1 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -619,6 +619,16 @@ public class StorageManager {
private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
/**
+ * Return the number of available bytes until the given path is considered
+ * running low on storage.
+ *
+ * @hide
+ */
+ public long getStorageBytesUntilLow(File path) {
+ return path.getUsableSpace() - getStorageFullBytes(path);
+ }
+
+ /**
* Return the number of available bytes at which the given path is
* considered running low on storage.
*
diff --git a/core/java/android/view/WindowCallbackWrapper.java b/core/java/android/view/WindowCallbackWrapper.java
new file mode 100644
index 0000000..35a6a76
--- /dev/null
+++ b/core/java/android/view/WindowCallbackWrapper.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2014 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 android.view;
+
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * A simple decorator stub for Window.Callback that passes through any calls
+ * to the wrapped instance as a base implementation. Call super.foo() to call into
+ * the wrapped callback for any subclasses.
+ *
+ * @hide for internal use
+ */
+public class WindowCallbackWrapper implements Window.Callback {
+ private Window.Callback mWrapped;
+
+ public WindowCallbackWrapper(Window.Callback wrapped) {
+ if (wrapped == null) {
+ throw new IllegalArgumentException("Window callback may not be null");
+ }
+ mWrapped = wrapped;
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ return mWrapped.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public boolean dispatchKeyShortcutEvent(KeyEvent event) {
+ return mWrapped.dispatchKeyShortcutEvent(event);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ return mWrapped.dispatchTouchEvent(event);
+ }
+
+ @Override
+ public boolean dispatchTrackballEvent(MotionEvent event) {
+ return mWrapped.dispatchTrackballEvent(event);
+ }
+
+ @Override
+ public boolean dispatchGenericMotionEvent(MotionEvent event) {
+ return mWrapped.dispatchGenericMotionEvent(event);
+ }
+
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ return mWrapped.dispatchPopulateAccessibilityEvent(event);
+ }
+
+ @Override
+ public View onCreatePanelView(int featureId) {
+ return mWrapped.onCreatePanelView(featureId);
+ }
+
+ @Override
+ public boolean onCreatePanelMenu(int featureId, Menu menu) {
+ return mWrapped.onCreatePanelMenu(featureId, menu);
+ }
+
+ @Override
+ public boolean onPreparePanel(int featureId, View view, Menu menu) {
+ return mWrapped.onPreparePanel(featureId, view, menu);
+ }
+
+ @Override
+ public boolean onMenuOpened(int featureId, Menu menu) {
+ return mWrapped.onMenuOpened(featureId, menu);
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ return mWrapped.onMenuItemSelected(featureId, item);
+ }
+
+ @Override
+ public void onWindowAttributesChanged(WindowManager.LayoutParams attrs) {
+ mWrapped.onWindowAttributesChanged(attrs);
+ }
+
+ @Override
+ public void onContentChanged() {
+ mWrapped.onContentChanged();
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ mWrapped.onWindowFocusChanged(hasFocus);
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ mWrapped.onAttachedToWindow();
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ mWrapped.onDetachedFromWindow();
+ }
+
+ @Override
+ public void onPanelClosed(int featureId, Menu menu) {
+ mWrapped.onPanelClosed(featureId, menu);
+ }
+
+ @Override
+ public boolean onSearchRequested() {
+ return mWrapped.onSearchRequested();
+ }
+
+ @Override
+ public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
+ return mWrapped.onWindowStartingActionMode(callback);
+ }
+
+ @Override
+ public void onActionModeStarted(ActionMode mode) {
+ mWrapped.onActionModeStarted(mode);
+ }
+
+ @Override
+ public void onActionModeFinished(ActionMode mode) {
+ mWrapped.onActionModeFinished(mode);
+ }
+}
+
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index c0961fd..06b7a93 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -79,6 +79,7 @@ class FastScroller {
// Positions for preview image and text.
private static final int OVERLAY_FLOATING = 0;
private static final int OVERLAY_AT_THUMB = 1;
+ private static final int OVERLAY_ABOVE_THUMB = 2;
// Indices for mPreviewResId.
private static final int PREVIEW_LEFT = 0;
@@ -189,8 +190,9 @@ class FastScroller {
/**
* Position for the preview image and text. One of:
* <ul>
- * <li>{@link #OVERLAY_AT_THUMB}
* <li>{@link #OVERLAY_FLOATING}
+ * <li>{@link #OVERLAY_AT_THUMB}
+ * <li>{@link #OVERLAY_ABOVE_THUMB}
* </ul>
*/
private int mOverlayPosition;
@@ -310,8 +312,10 @@ class FastScroller {
final int textMinSize = Math.max(0, mPreviewMinHeight);
mPrimaryText.setMinimumWidth(textMinSize);
mPrimaryText.setMinimumHeight(textMinSize);
+ mPrimaryText.setIncludeFontPadding(false);
mSecondaryText.setMinimumWidth(textMinSize);
mSecondaryText.setMinimumHeight(textMinSize);
+ mSecondaryText.setIncludeFontPadding(false);
refreshDrawablePressedState();
}
@@ -595,10 +599,10 @@ class FastScroller {
margins.right = mPreviewImage.getPaddingRight();
margins.bottom = mPreviewImage.getPaddingBottom();
- if (mOverlayPosition == OVERLAY_AT_THUMB) {
- measureViewToSide(v, mThumbImage, margins, out);
- } else {
+ if (mOverlayPosition == OVERLAY_FLOATING) {
measureFloating(v, margins, out);
+ } else {
+ measureViewToSide(v, mThumbImage, margins, out);
}
}
@@ -1147,11 +1151,23 @@ class FastScroller {
final float thumbMiddle = position * range + offset;
thumbImage.setTranslationY(thumbMiddle - thumbImage.getHeight() / 2);
- final float previewPos = mOverlayPosition == OVERLAY_AT_THUMB ? thumbMiddle : 0;
-
- // Center the preview on the thumb, constrained to the list bounds.
final View previewImage = mPreviewImage;
final float previewHalfHeight = previewImage.getHeight() / 2f;
+ final float previewPos;
+ switch (mOverlayPosition) {
+ case OVERLAY_AT_THUMB:
+ previewPos = thumbMiddle;
+ break;
+ case OVERLAY_ABOVE_THUMB:
+ previewPos = thumbMiddle - previewHalfHeight;
+ break;
+ case OVERLAY_FLOATING:
+ default:
+ previewPos = 0;
+ break;
+ }
+
+ // Center the preview on the thumb, constrained to the list bounds.
final float minP = top + previewHalfHeight;
final float maxP = bottom - previewHalfHeight;
final float previewMiddle = MathUtils.constrain(previewPos, minP, maxP);
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
index 7a3ffdf..81ea191 100644
--- a/core/java/com/android/internal/app/IMediaContainerService.aidl
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -25,10 +25,7 @@ interface IMediaContainerService {
boolean isExternal, boolean isForwardLocked, String abiOverride);
int copyPackage(String packagePath, in IParcelFileDescriptorFactory target);
- PackageInfoLite getMinimalPackageInfo(String packagePath, int flags, long threshold,
- String abiOverride);
- boolean checkInternalFreeStorage(String packagePath, boolean isForwardLocked, long threshold);
- boolean checkExternalFreeStorage(String packagePath, boolean isForwardLocked, String abiOverride);
+ PackageInfoLite getMinimalPackageInfo(String packagePath, int flags, String abiOverride);
ObbInfo getObbInfo(String filename);
long calculateDirectorySize(String directory);
/** Return file system stats: [0] is total bytes, [1] is available bytes */
diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java
index 96d7192..13eb182 100644
--- a/core/java/com/android/internal/app/ToolbarActionBar.java
+++ b/core/java/com/android/internal/app/ToolbarActionBar.java
@@ -29,6 +29,7 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
+import android.view.WindowCallbackWrapper;
import android.widget.SpinnerAdapter;
import android.widget.Toolbar;
import com.android.internal.view.menu.MenuBuilder;
@@ -40,6 +41,7 @@ import java.util.ArrayList;
public class ToolbarActionBar extends ActionBar {
private Toolbar mToolbar;
private DecorToolbar mDecorToolbar;
+ private boolean mToolbarMenuPrepared;
private Window.Callback mWindowCallback;
private boolean mLastMenuVisibility;
@@ -64,12 +66,16 @@ public class ToolbarActionBar extends ActionBar {
public ToolbarActionBar(Toolbar toolbar, CharSequence title, Window.Callback windowCallback) {
mToolbar = toolbar;
mDecorToolbar = new ToolbarWidgetWrapper(toolbar, false);
- mWindowCallback = windowCallback;
+ mWindowCallback = new ToolbarCallbackWrapper(windowCallback);
mDecorToolbar.setWindowCallback(mWindowCallback);
toolbar.setOnMenuItemClickListener(mMenuClicker);
mDecorToolbar.setWindowTitle(title);
}
+ public Window.Callback getWrappedWindowCallback() {
+ return mWindowCallback;
+ }
+
@Override
public void setCustomView(View view) {
setCustomView(view, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
@@ -465,4 +471,20 @@ public class ToolbarActionBar extends ActionBar {
mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible);
}
}
+
+ private class ToolbarCallbackWrapper extends WindowCallbackWrapper {
+ public ToolbarCallbackWrapper(Window.Callback wrapped) {
+ super(wrapped);
+ }
+
+ @Override
+ public boolean onPreparePanel(int featureId, View view, Menu menu) {
+ final boolean result = super.onPreparePanel(featureId, view, menu);
+ if (result && !mToolbarMenuPrepared) {
+ mDecorToolbar.setMenuPrepared();
+ mToolbarMenuPrepared = true;
+ }
+ return result;
+ }
+ }
}
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index eff6684..fd96f64 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -16,14 +16,21 @@
package com.android.internal.content;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Environment;
import android.os.FileUtils;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.storage.IMountService;
+import android.os.storage.StorageManager;
import android.os.storage.StorageResultCode;
import android.util.Log;
+import libcore.io.IoUtils;
+
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -33,8 +40,6 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
-import libcore.io.IoUtils;
-
/**
* Constants used internally between the PackageManager
* and media container service transports.
@@ -298,4 +303,78 @@ public class PackageHelper {
}
return false;
}
+
+ /**
+ * Given a requested {@link PackageInfo#installLocation} and calculated
+ * install size, pick the actual location to install the app.
+ */
+ public static int resolveInstallLocation(Context context, int installLocation, long sizeBytes,
+ int installFlags) {
+ final int prefer;
+ final boolean checkBoth;
+ if ((installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
+ prefer = RECOMMEND_INSTALL_INTERNAL;
+ checkBoth = false;
+ } else if ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
+ prefer = RECOMMEND_INSTALL_EXTERNAL;
+ checkBoth = false;
+ } else if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+ prefer = RECOMMEND_INSTALL_INTERNAL;
+ checkBoth = false;
+ } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
+ prefer = RECOMMEND_INSTALL_EXTERNAL;
+ checkBoth = true;
+ } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
+ prefer = RECOMMEND_INSTALL_INTERNAL;
+ checkBoth = true;
+ } else {
+ prefer = RECOMMEND_INSTALL_INTERNAL;
+ checkBoth = false;
+ }
+
+ final boolean emulated = Environment.isExternalStorageEmulated();
+ final StorageManager storage = StorageManager.from(context);
+
+ boolean fitsOnInternal = false;
+ if (checkBoth || prefer == RECOMMEND_INSTALL_INTERNAL) {
+ fitsOnInternal = (sizeBytes
+ <= storage.getStorageBytesUntilLow(Environment.getDataDirectory()));
+ }
+
+ boolean fitsOnExternal = false;
+ if (!emulated && (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL)) {
+ fitsOnExternal = (sizeBytes
+ <= storage.getStorageBytesUntilLow(Environment.getExternalStorageDirectory()));
+ }
+
+ if (prefer == RECOMMEND_INSTALL_INTERNAL) {
+ if (fitsOnInternal) {
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ }
+ } else if (!emulated && prefer == RECOMMEND_INSTALL_EXTERNAL) {
+ if (fitsOnExternal) {
+ return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ }
+ }
+
+ if (checkBoth) {
+ if (fitsOnInternal) {
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ } else if (!emulated && fitsOnExternal) {
+ return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
+ }
+ }
+
+ /*
+ * If they requested to be on the external media by default, return that
+ * the media was unavailable. Otherwise, indicate there was insufficient
+ * storage space available.
+ */
+ if (!emulated && (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL)
+ && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
+ return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE;
+ } else {
+ return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
+ }
+ }
}
diff --git a/core/res/res/drawable/fastscroll_label_left_material.xml b/core/res/res/drawable/fastscroll_label_left_material.xml
new file mode 100644
index 0000000..430d1b0
--- /dev/null
+++ b/core/res/res/drawable/fastscroll_label_left_material.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners
+ android:topLeftRadius="44dp"
+ android:topRightRadius="44dp"
+ android:bottomRightRadius="44dp" />
+ <padding
+ android:paddingLeft="22dp"
+ android:paddingRight="22dp" />
+ <solid android:color="?attr/colorControlActivated" />
+</shape>
diff --git a/core/res/res/drawable/fastscroll_label_right_material.xml b/core/res/res/drawable/fastscroll_label_right_material.xml
new file mode 100644
index 0000000..6e61397
--- /dev/null
+++ b/core/res/res/drawable/fastscroll_label_right_material.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners
+ android:topLeftRadius="44dp"
+ android:topRightRadius="44dp"
+ android:bottomLeftRadius="44dp" />
+ <padding
+ android:paddingLeft="22dp"
+ android:paddingRight="22dp" />
+ <solid android:color="?attr/colorControlActivated" />
+</shape>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 89bda82..f9ea5d8 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -733,6 +733,7 @@
<attr name="fastScrollOverlayPosition">
<enum name="floating" value="0" />
<enum name="atThumb" value="1" />
+ <enum name="aboveThumb" value="2" />
</attr>
<!-- Text color for the fast scroll index overlay. Make sure it
plays nicely with fastScrollPreviewBackground[Left|Right]. -->
@@ -3196,6 +3197,8 @@
<enum name="floating" value="0" />
<!-- Pinned alongside the thumb. -->
<enum name="atThumb" value="1" />
+ <!-- Pinned above the thumb. -->
+ <enum name="aboveThumb" value="2" />
</attr>
<attr name="textAppearance" />
<attr name="textColor" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index bf97ca5..f702b95 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1656,4 +1656,11 @@
<!-- Package name providing WebView implementation. -->
<string name="config_webViewPackageName" translatable="false">com.android.webview</string>
+ <!-- If EMS is not supported, framework breaks down EMS into single segment SMS
+ and adds page info " x/y". This config is used to set which carrier doesn't
+ support EMS and whether page info should be added at the beginning or the end.
+ We use tag 'prefix' for position beginning and 'suffix' for position end.
+ Examples: <item>311480;prefix</item> <item>310260;suffix</item>
+ -->
+ <string-array translatable="false" name="no_ems_support_sim_operators" />
</resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 0f27824..298fea3 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -877,6 +877,10 @@ please see styles_device_defaults.xml.
<style name="Widget.Material.FastScroll" parent="Widget.FastScroll">
<item name="thumbMinWidth">0dp</item>
<item name="thumbMinHeight">0dp</item>
+ <item name="minWidth">88dp</item>
+ <item name="minHeight">88dp</item>
+ <item name="padding">0dp</item>
+ <item name="textSize">45sp</item>
</style>
<style name="Widget.Material.PreferenceFrameLayout">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 426a82d..1cb7b17 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1982,4 +1982,5 @@
<java-symbol type="attr" name="checkMarkGravity" />
<java-symbol type="layout" name="select_dialog_singlechoice_material" />
<java-symbol type="layout" name="select_dialog_multichoice_material" />
+ <java-symbol type="array" name="no_ems_support_sim_operators" />
</resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index b285797..18170ac 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -364,10 +364,10 @@ please see themes_device_defaults.xml.
<!-- TODO: This belongs in a FastScroll style -->
<item name="fastScrollThumbDrawable">@drawable/fastscroll_thumb_material</item>
- <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_dark</item>
- <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_dark</item>
+ <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_material</item>
+ <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_material</item>
<item name="fastScrollTrackDrawable">@drawable/fastscroll_track_material</item>
- <item name="fastScrollOverlayPosition">atThumb</item>
+ <item name="fastScrollOverlayPosition">aboveThumb</item>
<!-- Color palette -->
<item name="colorPrimaryDark">@color/material_blue_grey_900</item>
@@ -706,10 +706,10 @@ please see themes_device_defaults.xml.
<item name="datePickerDialogTheme">?attr/dialogTheme</item>
<item name="fastScrollThumbDrawable">@drawable/fastscroll_thumb_material</item>
- <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_light</item>
- <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_light</item>
+ <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_material</item>
+ <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_material</item>
<item name="fastScrollTrackDrawable">@drawable/fastscroll_track_material</item>
- <item name="fastScrollOverlayPosition">atThumb</item>
+ <item name="fastScrollOverlayPosition">aboveThumb</item>
<!-- Color palette -->
<item name="colorPrimaryDark">@color/material_blue_grey_100</item>
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 5318fa7..d87e8e4 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -114,7 +114,9 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
final boolean changed = super.setVisible(visible, restart);
if (visible) {
if (restart || changed) {
- setFrame(restart ? 0 : mCurFrame, true, mAnimating);
+ boolean startFromZero = restart || mCurFrame < 0 ||
+ mCurFrame >= mAnimationState.getChildCount();
+ setFrame(startFromZero ? 0 : mCurFrame, true, mAnimating);
}
} else {
unscheduleSelf(this);
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index 588e776..dd0f06f 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -26,10 +26,13 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
-import android.graphics.*;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Insets;
+import android.graphics.Outline;
import android.graphics.PorterDuff.Mode;
+import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.Log;
import java.io.IOException;
@@ -50,8 +53,6 @@ import java.io.IOException;
* @attr ref android.R.styleable#InsetDrawable_insetBottom
*/
public class InsetDrawable extends Drawable implements Drawable.Callback {
- private static final String LOG_TAG = "InsetDrawable";
-
private final Rect mTmpRect = new Rect();
private InsetState mInsetState;
@@ -86,7 +87,6 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
final TypedArray a = r.obtainAttributes(attrs, R.styleable.InsetDrawable);
super.inflateWithAttributes(r, parser, a, R.styleable.InsetDrawable_visible);
updateStateFromTypedArray(a);
- a.recycle();
// Load inner XML elements.
if (mInsetState.mDrawable == null) {
@@ -104,9 +104,17 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
dr.setCallback(this);
}
- // Verify state.
- if (mInsetState.mDrawable == null) {
- Log.w(LOG_TAG, "No drawable specified for <inset>");
+ verifyRequiredAttributes(a);
+ a.recycle();
+ }
+
+ private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
+ // If we're not waiting on a theme, verify required attributes.
+ if (mInsetState.mDrawable == null && (mInsetState.mThemeAttrs == null
+ || mInsetState.mThemeAttrs[R.styleable.InsetDrawable_drawable] == 0)) {
+ throw new XmlPullParserException(a.getPositionDescription() +
+ ": <inset> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
}
}
@@ -167,6 +175,7 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.InsetDrawable);
try {
updateStateFromTypedArray(a);
+ verifyRequiredAttributes(a);
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} finally {
@@ -224,12 +233,8 @@ public class InsetDrawable extends Drawable implements Drawable.Callback {
padding.top += mInsetState.mInsetTop;
padding.bottom += mInsetState.mInsetBottom;
- if (pad || (mInsetState.mInsetLeft | mInsetState.mInsetRight |
- mInsetState.mInsetTop | mInsetState.mInsetBottom) != 0) {
- return true;
- } else {
- return false;
- }
+ return pad || (mInsetState.mInsetLeft | mInsetState.mInsetRight |
+ mInsetState.mInsetTop | mInsetState.mInsetBottom) != 0;
}
/** @hide */
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 6a7433d..3f5697f 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -158,17 +158,17 @@ public final class TvInputInfo implements Parcelable {
}
/**
- * Create a new instance of the TvInputInfo class,
- * instantiating it from the given Context, ResolveInfo, and HdmiDeviceInfo.
+ * Create a new instance of the TvInputInfo class, instantiating it from the given Context,
+ * ResolveInfo, and HdmiDeviceInfo.
*
* @param service The ResolveInfo returned from the package manager about this TV input service.
* @param deviceInfo The HdmiDeviceInfo for a HDMI CEC logical device.
* @param parentId The ID of this TV input's parent input. {@code null} if none exists.
- * @param iconUri The {@link android.net.Uri} to load the icon image.
- * {@see android.content.ContentResolver#openInputStream}. If it is null, the application
- * icon of {@code service} will be loaded.
- * @param label The label of this TvInputInfo. If it is null or empty, {@code service} label
- * will be loaded.
+ * @param iconUri The {@link android.net.Uri} to load the icon image. See
+ * {@link android.content.ContentResolver#openInputStream}. If it is {@code null},
+ * the application icon of {@code service} will be loaded.
+ * @param label The label of this TvInputInfo. If it is {@code null} or empty, {@code service}
+ * label will be loaded.
* @hide
*/
@SystemApi
@@ -182,16 +182,16 @@ public final class TvInputInfo implements Parcelable {
}
/**
- * Create a new instance of the TvInputInfo class,
- * instantiating it from the given Context, ResolveInfo, and TvInputHardwareInfo.
+ * Create a new instance of the TvInputInfo class, instantiating it from the given Context,
+ * ResolveInfo, and TvInputHardwareInfo.
*
* @param service The ResolveInfo returned from the package manager about this TV input service.
* @param hardwareInfo The TvInputHardwareInfo for a TV input hardware device.
- * @param iconUri The {@link android.net.Uri} to load the icon image.
- * {@see android.content.ContentResolver#openInputStream}. If it is null, the application
- * icon of {@code service} will be loaded.
- * @param label The label of this TvInputInfo. If it is null or empty, {@code service} label
- * will be loaded.
+ * @param iconUri The {@link android.net.Uri} to load the icon image. See
+ * {@link android.content.ContentResolver#openInputStream}. If it is {@code null},
+ * the application icon of {@code service} will be loaded.
+ * @param label The label of this TvInputInfo. If it is {@code null} or empty, {@code service}
+ * label will be loaded.
* @hide
*/
@SystemApi
@@ -620,7 +620,7 @@ public final class TvInputInfo implements Parcelable {
return new HashSet<String>();
}
String[] ids = hiddenIdsString.split(TV_INPUT_SEPARATOR);
- return new HashSet(Arrays.asList(ids));
+ return new HashSet<>(Arrays.asList(ids));
}
/**
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 6e075b2..f76c78b 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -235,14 +235,14 @@ public final class TvInputManager {
}
/**
- * This is called when {@link TvInputService.Session#layoutSurface} is called to
- * change the layout of surface.
+ * This is called when {@link TvInputService.Session#layoutSurface} is called to change the
+ * layout of surface.
*
* @param session A {@link TvInputManager.Session} associated with this callback
- * @param l Left position.
- * @param t Top position.
- * @param r Right position.
- * @param b Bottom position.
+ * @param left Left position.
+ * @param top Top position.
+ * @param right Right position.
+ * @param bottom Bottom position.
* @hide
*/
@SystemApi
@@ -1164,7 +1164,7 @@ public final class TvInputManager {
* {@link TvTrackInfo#TYPE_SUBTITLE}.
* @param trackId The ID of the track to select. When {@code null}, the currently selected
* track of the given type will be unselected.
- * @see #getTracks()
+ * @see #getTracks
*/
public void selectTrack(int type, String trackId) {
if (type == TvTrackInfo.TYPE_AUDIO) {
@@ -1462,14 +1462,14 @@ public final class TvInputManager {
// Assumes the event has already been removed from the queue.
void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
p.mHandled = handled;
- if (p.mHandler.getLooper().isCurrentThread()) {
+ if (p.mEventHandler.getLooper().isCurrentThread()) {
// Already running on the callback handler thread so we can send the callback
// immediately.
p.run();
} else {
// Post the event to the callback handler thread.
// In this case, the callback will be responsible for recycling the event.
- Message msg = Message.obtain(p.mHandler, p);
+ Message msg = Message.obtain(p.mEventHandler, p);
msg.setAsynchronous(true);
msg.sendToTarget();
}
@@ -1494,9 +1494,9 @@ public final class TvInputManager {
p = new PendingEvent();
}
p.mEvent = event;
- p.mToken = token;
+ p.mEventToken = token;
p.mCallback = callback;
- p.mHandler = handler;
+ p.mEventHandler = handler;
return p;
}
@@ -1568,24 +1568,24 @@ public final class TvInputManager {
private final class PendingEvent implements Runnable {
public InputEvent mEvent;
- public Object mToken;
+ public Object mEventToken;
public FinishedInputEventCallback mCallback;
- public Handler mHandler;
+ public Handler mEventHandler;
public boolean mHandled;
public void recycle() {
mEvent = null;
- mToken = null;
+ mEventToken = null;
mCallback = null;
- mHandler = null;
+ mEventHandler = null;
mHandled = false;
}
@Override
public void run() {
- mCallback.onFinishedInputEvent(mToken, mHandled);
+ mCallback.onFinishedInputEvent(mEventToken, mHandled);
- synchronized (mHandler) {
+ synchronized (mEventHandler) {
recyclePendingEventLocked(this);
}
}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 8ffe6cc..c93b261 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -504,12 +504,13 @@ public abstract class TvInputService extends Service {
/**
* Assigns a position of the {@link Surface} passed by {@link #onSetSurface}. The position
- * is relative to an overlay view. {@see #onOverlayViewSizeChanged}.
+ * is relative to an overlay view.
*
* @param left Left position in pixels, relative to the overlay view.
* @param top Top position in pixels, relative to the overlay view.
* @param right Right position in pixels, relative to the overlay view.
* @param bottm Bottom position in pixels, relative to the overlay view.
+ * @see #onOverlayViewSizeChanged
* @hide
*/
@SystemApi
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 20a621b..4c225c1 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -16,11 +16,13 @@
package com.android.defcontainer;
+import static android.net.TrafficStats.MB_IN_BYTES;
+
import android.app.IntentService;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageCleanItem;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
@@ -37,8 +39,6 @@ import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.StatFs;
-import android.provider.Settings;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStatVfs;
@@ -50,13 +50,11 @@ import com.android.internal.content.PackageHelper;
import com.android.internal.os.IParcelFileDescriptorFactory;
import com.android.internal.util.ArrayUtils;
-import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
import libcore.io.Streams;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -155,10 +153,12 @@ public class DefaultContainerService extends IntentService {
* containing one or more APKs.
*/
@Override
- public PackageInfoLite getMinimalPackageInfo(final String packagePath, int flags,
- long threshold, String abiOverride) {
- PackageInfoLite ret = new PackageInfoLite();
+ public PackageInfoLite getMinimalPackageInfo(String packagePath, int flags,
+ String abiOverride) {
+ final Context context = DefaultContainerService.this;
+ final boolean isForwardLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
+ PackageInfoLite ret = new PackageInfoLite();
if (packagePath == null) {
Slog.i(TAG, "Invalid package file " + packagePath);
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK;
@@ -167,10 +167,12 @@ public class DefaultContainerService extends IntentService {
final File packageFile = new File(packagePath);
final PackageParser.PackageLite pkg;
+ final long sizeBytes;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
- } catch (PackageParserException e) {
- Slog.w(TAG, "Failed to parse package at " + packagePath);
+ sizeBytes = calculateInstalledSizeInner(pkg, isForwardLocked, abiOverride);
+ } catch (PackageParserException | IOException e) {
+ Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e);
if (!packageFile.exists()) {
ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
@@ -185,55 +187,13 @@ public class DefaultContainerService extends IntentService {
ret.versionCode = pkg.versionCode;
ret.installLocation = pkg.installLocation;
ret.verifiers = pkg.verifiers;
- ret.recommendedInstallLocation = recommendAppInstallLocation(pkg, flags, threshold,
- abiOverride);
+ ret.recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
+ pkg.installLocation, sizeBytes, flags);
ret.multiArch = pkg.multiArch;
return ret;
}
- /**
- * Determine if package will fit on internal storage.
- *
- * @param packagePath absolute path to the package to be copied. Can be
- * a single monolithic APK file or a cluster directory
- * containing one or more APKs.
- */
- @Override
- public boolean checkInternalFreeStorage(String packagePath, boolean isForwardLocked,
- long threshold) throws RemoteException {
- final File packageFile = new File(packagePath);
- final PackageParser.PackageLite pkg;
- try {
- pkg = PackageParser.parsePackageLite(packageFile, 0);
- return isUnderInternalThreshold(pkg, isForwardLocked, threshold);
- } catch (PackageParserException | IOException e) {
- Slog.w(TAG, "Failed to parse package at " + packagePath);
- return false;
- }
- }
-
- /**
- * Determine if package will fit on external storage.
- *
- * @param packagePath absolute path to the package to be copied. Can be
- * a single monolithic APK file or a cluster directory
- * containing one or more APKs.
- */
- @Override
- public boolean checkExternalFreeStorage(String packagePath, boolean isForwardLocked,
- String abiOverride) throws RemoteException {
- final File packageFile = new File(packagePath);
- final PackageParser.PackageLite pkg;
- try {
- pkg = PackageParser.parsePackageLite(packageFile, 0);
- return isUnderExternalThreshold(pkg, isForwardLocked, abiOverride);
- } catch (PackageParserException | IOException e) {
- Slog.w(TAG, "Failed to parse package at " + packagePath);
- return false;
- }
- }
-
@Override
public ObbInfo getObbInfo(String filename) {
try {
@@ -295,13 +255,10 @@ public class DefaultContainerService extends IntentService {
final PackageParser.PackageLite pkg;
try {
pkg = PackageParser.parsePackageLite(packageFile, 0);
- return calculateContainerSize(pkg, isForwardLocked, abiOverride) * 1024 * 1024;
+ return calculateInstalledSizeInner(pkg, isForwardLocked, abiOverride);
} catch (PackageParserException | IOException e) {
- /*
- * Okay, something failed, so let's just estimate it to be 2x
- * the file size. Note this will be 0 if the file doesn't exist.
- */
- return packageFile.length() * 2;
+ Slog.w(TAG, "Failed to calculate installed size: " + e);
+ return Long.MAX_VALUE;
}
}
};
@@ -381,10 +338,12 @@ public class DefaultContainerService extends IntentService {
return null;
}
- // Calculate size of container needed to hold base APK.
+ // Calculate size of container needed to hold base APK. Round up to
+ // nearest MB, and tack on an extra MB for filesystem overhead.
final int sizeMb;
try {
- sizeMb = calculateContainerSize(pkg, handle, isForwardLocked, abis);
+ final long sizeBytes = calculateInstalledSizeInner(pkg, handle, isForwardLocked, abis);
+ sizeMb = ((int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES)) + 1;
} catch (IOException e) {
Slog.w(TAG, "Problem when trying to copy " + codeFile.getPath());
return null;
@@ -523,124 +482,23 @@ public class DefaultContainerService extends IntentService {
}
}
- private static final int PREFER_INTERNAL = 1;
- private static final int PREFER_EXTERNAL = 2;
-
- private int recommendAppInstallLocation(PackageLite pkg, int flags, long threshold,
- String abiOverride) {
- int prefer;
- boolean checkBoth = false;
-
- final boolean isForwardLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
-
- check_inner : {
- /*
- * Explicit install flags should override the manifest settings.
- */
- if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
- prefer = PREFER_INTERNAL;
- break check_inner;
- } else if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
- prefer = PREFER_EXTERNAL;
- break check_inner;
- }
-
- /* No install flags. Check for manifest option. */
- if (pkg.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
- prefer = PREFER_INTERNAL;
- break check_inner;
- } else if (pkg.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
- prefer = PREFER_EXTERNAL;
- checkBoth = true;
- break check_inner;
- } else if (pkg.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
- // We default to preferring internal storage.
- prefer = PREFER_INTERNAL;
- checkBoth = true;
- break check_inner;
- }
-
- // Pick user preference
- int installPreference = Settings.Global.getInt(getApplicationContext()
- .getContentResolver(),
- Settings.Global.DEFAULT_INSTALL_LOCATION,
- PackageHelper.APP_INSTALL_AUTO);
- if (installPreference == PackageHelper.APP_INSTALL_INTERNAL) {
- prefer = PREFER_INTERNAL;
- break check_inner;
- } else if (installPreference == PackageHelper.APP_INSTALL_EXTERNAL) {
- prefer = PREFER_EXTERNAL;
- break check_inner;
- }
-
- /*
- * Fall back to default policy of internal-only if nothing else is
- * specified.
- */
- prefer = PREFER_INTERNAL;
- }
-
- final boolean emulated = Environment.isExternalStorageEmulated();
-
- boolean fitsOnInternal = false;
- if (checkBoth || prefer == PREFER_INTERNAL) {
- try {
- fitsOnInternal = isUnderInternalThreshold(pkg, isForwardLocked, threshold);
- } catch (IOException e) {
- return PackageHelper.RECOMMEND_FAILED_INVALID_URI;
- }
- }
-
- boolean fitsOnSd = false;
- if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)) {
- try {
- fitsOnSd = isUnderExternalThreshold(pkg, isForwardLocked, abiOverride);
- } catch (IOException e) {
- return PackageHelper.RECOMMEND_FAILED_INVALID_URI;
- }
- }
-
- if (prefer == PREFER_INTERNAL) {
- if (fitsOnInternal) {
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
- }
- } else if (!emulated && prefer == PREFER_EXTERNAL) {
- if (fitsOnSd) {
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
- }
- }
-
- if (checkBoth) {
- if (fitsOnInternal) {
- return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
- } else if (!emulated && fitsOnSd) {
- return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
- }
- }
-
- /*
- * If they requested to be on the external media by default, return that
- * the media was unavailable. Otherwise, indicate there was insufficient
- * storage space available.
- */
- if (!emulated && (checkBoth || prefer == PREFER_EXTERNAL)
- && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
- return PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE;
- } else {
- return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
+ private long calculateInstalledSizeInner(PackageLite pkg, boolean isForwardLocked,
+ String abiOverride) throws IOException {
+ NativeLibraryHelper.Handle handle = null;
+ try {
+ handle = NativeLibraryHelper.Handle.create(pkg);
+ return calculateInstalledSizeInner(pkg, handle, isForwardLocked,
+ calculateAbiList(handle, abiOverride, pkg.multiArch));
+ } finally {
+ IoUtils.closeQuietly(handle);
}
}
- /**
- * Measure a file to see if it fits within the free space threshold.
- *
- * @param threshold byte threshold to compare against
- * @return true if file fits under threshold
- * @throws FileNotFoundException when APK does not exist
- */
- private boolean isUnderInternalThreshold(PackageLite pkg, boolean isForwardLocked,
- long threshold) throws IOException {
+ private long calculateInstalledSizeInner(PackageLite pkg, NativeLibraryHelper.Handle handle,
+ boolean isForwardLocked, String[] abis) throws IOException {
long sizeBytes = 0;
+
+ // Include raw APKs, and possibly unpacked resources
for (String codePath : pkg.getAllCodePaths()) {
sizeBytes += new File(codePath).length();
@@ -649,47 +507,12 @@ public class DefaultContainerService extends IntentService {
}
}
- final StatFs stat = new StatFs(Environment.getDataDirectory().getPath());
- final long availBytes = stat.getAvailableBytes();
- return (availBytes - sizeBytes) > threshold;
- }
-
- /**
- * Measure a file to see if it fits in the external free space.
- *
- * @return true if file fits
- * @throws IOException when file does not exist
- */
- private boolean isUnderExternalThreshold(PackageLite pkg, boolean isForwardLocked,
- String abiOverride) throws IOException {
- if (Environment.isExternalStorageEmulated()) {
- return false;
- }
-
- final int sizeMb = calculateContainerSize(pkg, isForwardLocked, abiOverride);
-
- final int availSdMb;
- if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
- final StatFs sdStats = new StatFs(Environment.getExternalStorageDirectory().getPath());
- final int blocksToMb = (1 << 20) / sdStats.getBlockSize();
- availSdMb = sdStats.getAvailableBlocks() * blocksToMb;
- } else {
- availSdMb = -1;
+ // Include all relevant native code
+ if (!ArrayUtils.isEmpty(abis)) {
+ sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(handle, abis);
}
- return availSdMb > sizeMb;
- }
-
- private int calculateContainerSize(PackageLite pkg, boolean isForwardLocked, String abiOverride)
- throws IOException {
- NativeLibraryHelper.Handle handle = null;
- try {
- handle = NativeLibraryHelper.Handle.create(pkg);
- return calculateContainerSize(pkg, handle, isForwardLocked,
- calculateAbiList(handle, abiOverride, pkg.multiArch));
- } finally {
- IoUtils.closeQuietly(handle);
- }
+ return sizeBytes;
}
private String[] calculateAbiList(NativeLibraryHelper.Handle handle, String abiOverride,
@@ -731,43 +554,4 @@ public class DefaultContainerService extends IntentService {
return null;
}
-
- /**
- * Calculate the container size for a package.
- *
- * @return size in megabytes (2^20 bytes)
- * @throws IOException when there is a problem reading the file
- */
- private int calculateContainerSize(PackageLite pkg, NativeLibraryHelper.Handle handle,
- boolean isForwardLocked, String[] abis) throws IOException {
- // Calculate size of container needed to hold APKs.
- long sizeBytes = 0;
- for (String codePath : pkg.getAllCodePaths()) {
- sizeBytes += new File(codePath).length();
-
- if (isForwardLocked) {
- sizeBytes += PackageHelper.extractPublicFiles(codePath, null);
- }
- }
-
- // Check all the native files that need to be copied and add that to the
- // container size.
- if (abis != null) {
- sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(handle, abis);
- }
-
- int sizeMb = (int) (sizeBytes >> 20);
- if ((sizeBytes - (sizeMb * 1024 * 1024)) > 0) {
- sizeMb++;
- }
-
- /*
- * Add buffer size because we don't have a good way to determine the
- * real FAT size. Your FAT size varies with how many directory entries
- * you need, how big the whole filesystem is, and other such headaches.
- */
- sizeMb++;
-
- return sizeMb;
- }
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index dca8ad4..0393518 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -19,6 +19,7 @@ package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_ALL_USERS;
import static android.content.pm.PackageManager.INSTALL_FROM_ADB;
import static android.content.pm.PackageManager.INSTALL_REPLACE_EXISTING;
+import static android.net.TrafficStats.MB_IN_BYTES;
import static com.android.internal.util.XmlUtils.readBitmapAttribute;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -42,6 +43,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
+import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageInstallerCallback;
import android.content.pm.IPackageInstallerSession;
@@ -65,6 +67,7 @@ import android.os.RemoteException;
import android.os.SELinux;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.StorageManager;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
@@ -75,13 +78,14 @@ import android.util.ExceptionUtils;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageHelper;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.IoThread;
-import com.android.server.pm.PackageInstallerSession.Snapshot;
import com.google.android.collect.Sets;
import libcore.io.IoUtils;
@@ -108,6 +112,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
// TODO: remove outstanding sessions when installer package goes away
// TODO: notify listeners in other users when package has been installed there
+ // TODO: purge expired sessions periodically in addition to at reboot
/** XML constants used in {@link #mSessionsFile} */
private static final String TAG_SESSIONS = "sessions";
@@ -117,6 +122,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
private static final String ATTR_CREATED_MILLIS = "createdMillis";
private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
+ private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
private static final String ATTR_SEALED = "sealed";
private static final String ATTR_MODE = "mode";
private static final String ATTR_INSTALL_FLAGS = "installFlags";
@@ -139,6 +145,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
private final Context mContext;
private final PackageManagerService mPm;
private final AppOpsManager mAppOps;
+ private final StorageManager mStorage;
private final File mStagingDir;
private final HandlerThread mInstallThread;
@@ -165,10 +172,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
@GuardedBy("mSessions")
private final SparseArray<PackageInstallerSession> mHistoricalSessions = new SparseArray<>();
+ /** Sessions allocated to legacy users */
+ @GuardedBy("mSessions")
+ private final SparseBooleanArray mLegacySessions = new SparseBooleanArray();
+
private static final FilenameFilter sStageFilter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
- return name.startsWith("vmdl") && name.endsWith(".tmp");
+ return isStageName(name);
}
};
@@ -176,6 +187,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
mContext = context;
mPm = pm;
mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mStorage = StorageManager.from(mContext);
mStagingDir = stagingDir;
@@ -190,13 +202,16 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
synchronized (mSessions) {
readSessionsLocked();
- // Clean up orphaned staging directories
- final ArraySet<File> stages = Sets.newArraySet(mStagingDir.listFiles(sStageFilter));
+ final ArraySet<File> unclaimed = Sets.newArraySet(mStagingDir.listFiles(sStageFilter));
+
+ // Ignore stages claimed by active sessions
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
- stages.remove(session.sessionStageDir);
+ unclaimed.remove(session.internalStageDir);
}
- for (File stage : stages) {
+
+ // Clean up orphaned staging directories
+ for (File stage : unclaimed) {
Slog.w(TAG, "Deleting orphan stage " + stage);
if (stage.isDirectory()) {
FileUtils.deleteContents(stage);
@@ -206,22 +221,64 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
}
- public static boolean isStageFile(File file) {
- return sStageFilter.accept(null, file.getName());
+ public void onSecureContainersAvailable() {
+ synchronized (mSessions) {
+ final ArraySet<String> unclaimed = new ArraySet<>();
+ for (String cid : PackageHelper.getSecureContainerList()) {
+ if (isStageName(cid)) {
+ unclaimed.add(cid);
+ }
+ }
+
+ // Ignore stages claimed by active sessions
+ for (int i = 0; i < mSessions.size(); i++) {
+ final PackageInstallerSession session = mSessions.valueAt(i);
+ final String cid = session.externalStageCid;
+
+ if (unclaimed.remove(cid)) {
+ // Claimed by active session, mount it
+ PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
+ Process.SYSTEM_UID);
+ }
+ }
+
+ // Clean up orphaned staging containers
+ for (String cid : unclaimed) {
+ Slog.w(TAG, "Deleting orphan container " + cid);
+ PackageHelper.destroySdDir(cid);
+ }
+ }
+ }
+
+ public static boolean isStageName(String name) {
+ final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
+ final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
+ final boolean isLegacyContainer = name.startsWith("smdl2tmp");
+ return isFile || isContainer || isLegacyContainer;
}
@Deprecated
- public File allocateSessionDir() throws IOException {
+ public File allocateInternalStageDirLegacy() throws IOException {
synchronized (mSessions) {
try {
final int sessionId = allocateSessionIdLocked();
- return prepareSessionStageDir(sessionId);
+ mLegacySessions.put(sessionId, true);
+ return prepareInternalStageDir(sessionId);
} catch (IllegalStateException e) {
throw new IOException(e);
}
}
}
+ @Deprecated
+ public String allocateExternalStageCidLegacy() {
+ synchronized (mSessions) {
+ final int sessionId = allocateSessionIdLocked();
+ mLegacySessions.put(sessionId, true);
+ return "smdl" + sessionId + ".tmp";
+ }
+ }
+
private void readSessionsLocked() {
if (LOGD) Slog.v(TAG, "readSessionsLocked()");
@@ -246,9 +303,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
Slog.w(TAG, "Abandoning old session first created at "
+ session.createdMillis);
valid = false;
- } else if (!session.sessionStageDir.exists()) {
- Slog.w(TAG, "Abandoning session with missing stage "
- + session.sessionStageDir);
+ } else if (session.internalStageDir != null
+ && !session.internalStageDir.exists()) {
+ Slog.w(TAG, "Abandoning internal session with missing stage "
+ + session.internalStageDir);
valid = false;
} else {
valid = true;
@@ -281,7 +339,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
final int userId = readIntAttribute(in, ATTR_USER_ID);
final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
- final File sessionStageDir = new File(readStringAttribute(in, ATTR_SESSION_STAGE_DIR));
+ final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
+ final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
+ final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
final SessionParams params = new SessionParams(
@@ -299,7 +359,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
return new PackageInstallerSession(mInternalCallback, mContext, mPm,
mInstallThread.getLooper(), sessionId, userId, installerPackageName, params,
- createdMillis, sessionStageDir, sealed);
+ createdMillis, stageDir, stageCid, sealed);
}
private void writeSessionsLocked() {
@@ -332,7 +392,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
private void writeSessionLocked(XmlSerializer out, PackageInstallerSession session)
throws IOException {
final SessionParams params = session.params;
- final Snapshot snapshot = session.snapshot();
out.startTag(null, TAG_SESSION);
@@ -341,9 +400,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
session.installerPackageName);
writeLongAttribute(out, ATTR_CREATED_MILLIS, session.createdMillis);
- writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
- session.sessionStageDir.getAbsolutePath());
- writeBooleanAttribute(out, ATTR_SEALED, snapshot.sealed);
+ if (session.internalStageDir != null) {
+ writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
+ session.internalStageDir.getAbsolutePath());
+ }
+ if (session.externalStageCid != null) {
+ writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.externalStageCid);
+ }
+ writeBooleanAttribute(out, ATTR_SEALED, session.isSealed());
writeIntAttribute(out, ATTR_MODE, params.mode);
writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
@@ -372,6 +436,15 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
@Override
public int createSession(SessionParams params, String installerPackageName, int userId) {
+ try {
+ return createSessionInternal(params, installerPackageName, userId);
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
+ }
+
+ private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
+ throws IOException {
final int callingUid = Binder.getCallingUid();
mPm.enforceCrossUserPermission(callingUid, userId, true, "createSession");
@@ -393,14 +466,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
params.installFlags |= INSTALL_REPLACE_EXISTING;
}
- switch (params.mode) {
- case SessionParams.MODE_FULL_INSTALL:
- case SessionParams.MODE_INHERIT_EXISTING:
- break;
- default:
- throw new IllegalArgumentException("Params must have valid mode set");
- }
-
// Defensively resize giant app icons
if (params.appIcon != null) {
final ActivityManager am = (ActivityManager) mContext.getSystemService(
@@ -413,13 +478,41 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
}
- // Sanity check that install could fit
- if (params.sizeBytes > 0) {
- try {
- mPm.freeStorage(params.sizeBytes);
- } catch (IOException e) {
- throw ExceptionUtils.wrap(e);
+ // Figure out where we're going to be staging session data
+ final boolean stageInternal;
+
+ if (params.mode == SessionParams.MODE_FULL_INSTALL) {
+ // Brand new install, use best resolved location. This also verifies
+ // that target has enough free space for the install.
+ final int resolved = PackageHelper.resolveInstallLocation(mContext,
+ params.installLocation, params.sizeBytes, params.installFlags);
+ if (resolved == PackageHelper.RECOMMEND_INSTALL_INTERNAL) {
+ stageInternal = true;
+ } else if (resolved == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
+ stageInternal = false;
+ } else {
+ throw new IOException("No storage with enough free space; res=" + resolved);
}
+
+ } else if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
+ // We always stage inheriting sessions on internal storage first,
+ // since we don't want to grow containers until we're sure that
+ // everything looks legit.
+ stageInternal = true;
+ checkInternalStorage(params.sizeBytes);
+
+ // If we have a good hunch we'll end up on external storage, verify
+ // free space there too.
+ final ApplicationInfo info = mPm.getApplicationInfo(params.appPackageName, 0,
+ userId);
+ if (info != null && (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
+ checkExternalStorage(params.sizeBytes);
+
+ throw new UnsupportedOperationException("TODO: finish fleshing out ASEC support");
+ }
+
+ } else {
+ throw new IllegalArgumentException("Invalid install mode: " + params.mode);
}
final int sessionId;
@@ -437,14 +530,21 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
"Too many historical sessions for UID " + callingUid);
}
+ final long createdMillis = System.currentTimeMillis();
sessionId = allocateSessionIdLocked();
- final long createdMillis = System.currentTimeMillis();
- final File sessionStageDir = prepareSessionStageDir(sessionId);
+ // We're staging to exactly one location
+ File stageDir = null;
+ String stageCid = null;
+ if (stageInternal) {
+ stageDir = prepareInternalStageDir(sessionId);
+ } else {
+ stageCid = prepareExternalStageCid(sessionId, params.sizeBytes);
+ }
session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
mInstallThread.getLooper(), sessionId, userId, installerPackageName, params,
- createdMillis, sessionStageDir, false);
+ createdMillis, stageDir, stageCid, false);
mSessions.put(sessionId, session);
}
@@ -453,6 +553,29 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
return sessionId;
}
+ private void checkInternalStorage(long sizeBytes) throws IOException {
+ if (sizeBytes <= 0) return;
+
+ final File target = Environment.getDataDirectory();
+ final long targetBytes = sizeBytes + mStorage.getStorageLowBytes(target);
+
+ mPm.freeStorage(targetBytes);
+ if (target.getUsableSpace() < targetBytes) {
+ throw new IOException("Not enough internal space to write " + sizeBytes + " bytes");
+ }
+ }
+
+ private void checkExternalStorage(long sizeBytes) throws IOException {
+ if (sizeBytes <= 0) return;
+
+ final File target = Environment.getExternalStorageDirectory();
+ final long targetBytes = sizeBytes + mStorage.getStorageLowBytes(target);
+
+ if (target.getUsableSpace() < targetBytes) {
+ throw new IOException("Not enough external space to write " + sizeBytes + " bytes");
+ }
+ }
+
@Override
public IPackageInstallerSession openSession(int sessionId) {
synchronized (mSessions) {
@@ -463,9 +586,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
if (!isCallingUidOwner(session)) {
throw new SecurityException("Caller has no access to session " + sessionId);
}
- if (session.openCount.getAndIncrement() == 0) {
- mCallbacks.notifySessionOpened(sessionId, session.userId);
- }
+ session.open();
return session;
}
}
@@ -475,7 +596,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
int sessionId;
do {
sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
- if (mSessions.get(sessionId) == null && mHistoricalSessions.get(sessionId) == null) {
+ if (mSessions.get(sessionId) == null && mHistoricalSessions.get(sessionId) == null
+ && !mLegacySessions.get(sessionId, false)) {
return sessionId;
}
} while (n++ < 32);
@@ -483,11 +605,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
throw new IllegalStateException("Failed to allocate session ID");
}
- private File prepareSessionStageDir(int sessionId) {
+ private File prepareInternalStageDir(int sessionId) throws IOException {
final File file = new File(mStagingDir, "vmdl" + sessionId + ".tmp");
if (file.exists()) {
- throw new IllegalStateException("Session dir already exists: " + file);
+ throw new IOException("Session dir already exists: " + file);
}
try {
@@ -495,16 +617,34 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
Os.chmod(file.getAbsolutePath(), 0755);
} catch (ErrnoException e) {
// This purposefully throws if directory already exists
- throw new IllegalStateException("Failed to prepare session dir", e);
+ throw new IOException("Failed to prepare session dir", e);
}
if (!SELinux.restorecon(file)) {
- throw new IllegalStateException("Failed to restorecon session dir");
+ throw new IOException("Failed to restorecon session dir");
}
return file;
}
+ private String prepareExternalStageCid(int sessionId, long sizeBytes) throws IOException {
+ if (sizeBytes <= 0) {
+ throw new IOException("Session must provide valid size for ASEC");
+ }
+
+ final String cid = "smdl" + sessionId + ".tmp";
+
+ // Round up to nearest MB, plus another MB for filesystem overhead
+ final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1;
+
+ if (PackageHelper.createSdDir(sizeMb, cid, PackageManagerService.getEncryptKey(),
+ Process.SYSTEM_UID, true) == null) {
+ throw new IOException("Failed to create ASEC");
+ }
+
+ return cid;
+ }
+
@Override
public SessionInfo getSessionInfo(int sessionId) {
synchronized (mSessions) {
@@ -643,7 +783,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
public void onUserActionRequired(Intent intent) {
final Intent fillIn = new Intent();
fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_USER_ACTION_REQUIRED);
+ PackageInstaller.STATUS_PENDING_USER_ACTION);
fillIn.putExtra(Intent.EXTRA_INTENT, intent);
try {
mTarget.sendIntent(mContext, 0, fillIn, null, null);
@@ -679,7 +819,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
public void onUserActionRequired(Intent intent) {
final Intent fillIn = new Intent();
fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_USER_ACTION_REQUIRED);
+ PackageInstaller.STATUS_PENDING_USER_ACTION);
fillIn.putExtra(Intent.EXTRA_INTENT, intent);
try {
mTarget.sendIntent(mContext, 0, fillIn, null, null);
@@ -817,6 +957,11 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
}
pw.println();
pw.decreaseIndent();
+
+ pw.println("Legacy install sessions:");
+ pw.increaseIndent();
+ pw.println(mLegacySessions.toString());
+ pw.decreaseIndent();
}
}
@@ -825,6 +970,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
}
+ public void onSessionOpened(PackageInstallerSession session) {
+ mCallbacks.notifySessionOpened(session.sessionId, session.userId);
+ }
+
public void onSessionClosed(PackageInstallerSession session) {
mCallbacks.notifySessionClosed(session.sessionId, session.userId);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5ef24f2..0616460 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
+import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
@@ -58,6 +59,7 @@ import android.util.MathUtils;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -79,7 +81,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// TODO: enforce INSTALL_ALLOW_TEST
// TODO: enforce INSTALL_ALLOW_DOWNGRADE
- // TODO: handle INSTALL_EXTERNAL, INSTALL_INTERNAL
// TODO: treat INHERIT_EXISTING as installExistingPackage()
@@ -93,12 +94,24 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final String installerPackageName;
final SessionParams params;
final long createdMillis;
- final File sessionStageDir;
+
+ /** Internal location where staged data is written. */
+ final File internalStageDir;
+ /** External container where staged data is written. */
+ final String externalStageCid;
+
+ /**
+ * When a {@link SessionParams#MODE_INHERIT_EXISTING} session is installed
+ * into an ASEC, this is the container where the stage is combined with the
+ * existing install.
+ */
+ // TODO: persist this cid once we start splicing
+ String combinedCid;
/** Note that UID is not persisted; it's always derived at runtime. */
final int installerUid;
- AtomicInteger openCount = new AtomicInteger();
+ private final AtomicInteger mOpenCount = new AtomicInteger();
private final Object mLock = new Object();
@@ -119,6 +132,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private int mFinalStatus;
private String mFinalMessage;
+ @GuardedBy("mLock")
+ private File mResolvedStageDir;
+
/**
* Path to the resolved base APK for this session, which may point at an APK
* inside the session (when the session defines the base), or it may point
@@ -165,7 +181,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
String installerPackageName, SessionParams params, long createdMillis,
- File sessionStageDir, boolean sealed) {
+ File internalStageDir, String externalStageCid, boolean sealed) {
mCallback = callback;
mContext = context;
mPm = pm;
@@ -176,7 +192,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
this.installerPackageName = installerPackageName;
this.params = params;
this.createdMillis = createdMillis;
- this.sessionStageDir = sessionStageDir;
+ this.internalStageDir = internalStageDir;
+ this.externalStageCid = externalStageCid;
mSealed = sealed;
@@ -195,23 +212,29 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
public SessionInfo generateInfo() {
final SessionInfo info = new SessionInfo();
-
- info.sessionId = sessionId;
- info.installerPackageName = installerPackageName;
- info.resolvedBaseCodePath = mResolvedBaseCodePath;
- info.progress = mProgress;
- info.sealed = mSealed;
- info.open = openCount.get() > 0;
-
- info.mode = params.mode;
- info.sizeBytes = params.sizeBytes;
- info.appPackageName = params.appPackageName;
- info.appIcon = params.appIcon;
- info.appLabel = params.appLabel;
-
+ synchronized (mLock) {
+ info.sessionId = sessionId;
+ info.installerPackageName = installerPackageName;
+ info.resolvedBaseCodePath = mResolvedBaseCodePath;
+ info.progress = mProgress;
+ info.sealed = mSealed;
+ info.open = mOpenCount.get() > 0;
+
+ info.mode = params.mode;
+ info.sizeBytes = params.sizeBytes;
+ info.appPackageName = params.appPackageName;
+ info.appIcon = params.appIcon;
+ info.appLabel = params.appLabel;
+ }
return info;
}
+ public boolean isSealed() {
+ synchronized (mLock) {
+ return mSealed;
+ }
+ }
+
private void assertNotSealed(String cookie) {
synchronized (mLock) {
if (mSealed) {
@@ -220,6 +243,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ /**
+ * Resolve the actual location where staged data should be written. This
+ * might point at an ASEC mount point, which is why we delay path resolution
+ * until someone actively works with the session.
+ */
+ private File getStageDir() throws IOException {
+ synchronized (mLock) {
+ if (mResolvedStageDir == null) {
+ if (internalStageDir != null) {
+ mResolvedStageDir = internalStageDir;
+ } else {
+ final String path = PackageHelper.getSdDir(externalStageCid);
+ if (path != null) {
+ mResolvedStageDir = new File(path);
+ } else {
+ throw new IOException(
+ "Failed to resolve container path for " + externalStageCid);
+ }
+ }
+ }
+ return mResolvedStageDir;
+ }
+ }
+
@Override
public void setClientProgress(float progress) {
synchronized (mLock) {
@@ -253,7 +300,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public String[] getNames() {
assertNotSealed("getNames");
- return sessionStageDir.list();
+ try {
+ return getStageDir().list();
+ } catch (IOException e) {
+ throw ExceptionUtils.wrap(e);
+ }
}
@Override
@@ -267,8 +318,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes)
throws IOException {
- // TODO: relay over to DCS when installing to ASEC
-
// Quick sanity check of state, and allocate a pipe for ourselves. We
// then do heavy disk allocation outside the lock, but this open pipe
// will block any attempted install transitions.
@@ -285,7 +334,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid name: " + name);
}
- final File target = new File(sessionStageDir, name);
+ final File target = new File(getStageDir(), name);
final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(),
O_CREAT | O_WRONLY, 0644);
@@ -331,7 +380,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid name: " + name);
}
- final File target = new File(sessionStageDir, name);
+ final File target = new File(getStageDir(), name);
final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), O_RDONLY, 0);
return new ParcelFileDescriptor(targetFd);
@@ -369,10 +418,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// beyond this point we may have hardlinks to the valid install
}
+ final File stageDir;
+ try {
+ stageDir = getStageDir();
+ } catch (IOException e) {
+ throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
+ "Failed to resolve stage dir", e);
+ }
+
// Verify that stage looks sane with respect to existing application.
// This currently only ensures packageName, versionCode, and certificate
// consistency.
- validateInstallLocked();
+ validateInstallLocked(stageDir);
Preconditions.checkNotNull(mPackageName);
Preconditions.checkNotNull(mSignatures);
@@ -394,7 +451,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Inherit any packages and native libraries from existing install that
// haven't been overridden.
if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
- spliceExistingFilesIntoStage();
+ // TODO: implement splicing into existing ASEC
+ spliceExistingFilesIntoStage(stageDir);
}
// TODO: surface more granular state from dexopt
@@ -418,7 +476,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
};
- mPm.installStage(mPackageName, this.sessionStageDir, localObserver, params,
+ // TODO: send ASEC cid if that's where we staged things
+ mPm.installStage(mPackageName, this.internalStageDir, null, localObserver, params,
installerPackageName, installerUid, new UserHandle(userId));
}
@@ -428,13 +487,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* <p>
* Renames package files in stage to match split names defined inside.
*/
- private void validateInstallLocked() throws PackageManagerException {
+ private void validateInstallLocked(File stageDir) throws PackageManagerException {
mPackageName = null;
mVersionCode = -1;
mSignatures = null;
mResolvedBaseCodePath = null;
- final File[] files = sessionStageDir.listFiles();
+ final File[] files = stageDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
}
@@ -480,7 +539,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Invalid filename: " + targetName);
}
- final File targetFile = new File(sessionStageDir, targetName);
+ final File targetFile = new File(stageDir, targetName);
if (!file.equals(targetFile)) {
file.renameTo(targetFile);
}
@@ -550,7 +609,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* Application is already installed; splice existing files that haven't been
* overridden into our stage.
*/
- private void spliceExistingFilesIntoStage() throws PackageManagerException {
+ private void spliceExistingFilesIntoStage(File stageDir) throws PackageManagerException {
final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
int n = 0;
@@ -559,7 +618,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
for (File oldFile : oldFiles) {
if (!PackageParser.isApkFile(oldFile)) continue;
- final File newFile = new File(sessionStageDir, oldFile.getName());
+ final File newFile = new File(stageDir, oldFile.getName());
try {
Os.link(oldFile.getAbsolutePath(), newFile.getAbsolutePath());
n++;
@@ -588,9 +647,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ public void open() {
+ if (mOpenCount.getAndIncrement() == 0) {
+ mCallback.onSessionOpened(this);
+ }
+ }
+
@Override
public void close() {
- if (openCount.decrementAndGet() == 0) {
+ if (mOpenCount.decrementAndGet() == 0) {
mCallback.onSessionClosed(this);
}
}
@@ -621,11 +686,22 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mSealed = true;
mDestroyed = true;
}
- FileUtils.deleteContents(sessionStageDir);
- sessionStageDir.delete();
+ if (internalStageDir != null) {
+ FileUtils.deleteContents(internalStageDir);
+ internalStageDir.delete();
+ }
+ if (externalStageCid != null) {
+ PackageHelper.destroySdDir(externalStageCid);
+ }
}
void dump(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ dumpLocked(pw);
+ }
+ }
+
+ private void dumpLocked(IndentingPrintWriter pw) {
pw.println("Session " + sessionId + ":");
pw.increaseIndent();
@@ -633,7 +709,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
pw.printPair("installerPackageName", installerPackageName);
pw.printPair("installerUid", installerUid);
pw.printPair("createdMillis", createdMillis);
- pw.printPair("sessionStageDir", sessionStageDir);
+ pw.printPair("internalStageDir", internalStageDir);
+ pw.printPair("externalStageCid", externalStageCid);
pw.println();
params.dump(pw);
@@ -650,18 +727,4 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
pw.decreaseIndent();
}
-
- Snapshot snapshot() {
- return new Snapshot(this);
- }
-
- static class Snapshot {
- final float clientProgress;
- final boolean sealed;
-
- public Snapshot(PackageInstallerSession session) {
- clientProgress = session.mClientProgress;
- sealed = session.mSealed;
- }
- }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b62c304..51559aa 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -140,6 +140,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Environment.UserEnvironment;
+import android.os.storage.StorageManager;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
@@ -211,6 +212,7 @@ import dalvik.system.StaleDexCacheError;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
/**
* Keep track of all those .apks everywhere.
@@ -311,8 +313,6 @@ public class PackageManagerService extends IPackageManager.Stub {
private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
- static final String mTempContainerPrefix = "smdl2tmp";
-
private static String sPreferredInstructionSet;
final ServiceThread mHandlerThread;
@@ -4101,21 +4101,24 @@ public class PackageManagerService extends IPackageManager.Stub {
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
- && !PackageInstallerService.isStageFile(file);
+ && !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
- // Ignore entries which are not apk's
+ // Ignore entries which are not packages
continue;
}
try {
- scanPackageLI(file, flags | PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
+ scanPackageLI(file, flags | PackageParser.PARSE_MUST_BE_APK,
+ scanMode, currentTime, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
- // Don't mess around with apps in system partition.
+ // Delete invalid userdata apps
if ((flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
- // Delete the apk
- Slog.w(TAG, "Cleaning up failed install of " + file);
+ Slog.w(TAG, "Deleting invalid package at " + file);
+ if (file.isDirectory()) {
+ FileUtils.deleteContents(file);
+ }
file.delete();
}
}
@@ -7839,19 +7842,19 @@ public class PackageManagerService extends IPackageManager.Stub {
verificationParams.setInstallerUid(uid);
final Message msg = mHandler.obtainMessage(INIT_COPY);
- msg.obj = new InstallParams(originFile, false, observer, filteredFlags,
+ msg.obj = new InstallParams(originFile, null, false, observer, filteredFlags,
installerPackageName, verificationParams, user, packageAbiOverride);
mHandler.sendMessage(msg);
}
- void installStage(String packageName, File stageDir, IPackageInstallObserver2 observer,
- PackageInstaller.SessionParams params, String installerPackageName, int installerUid,
- UserHandle user) {
+ void installStage(String packageName, File stagedDir, String stagedCid,
+ IPackageInstallObserver2 observer, PackageInstaller.SessionParams params,
+ String installerPackageName, int installerUid, UserHandle user) {
final VerificationParams verifParams = new VerificationParams(null, params.originatingUri,
params.referrerUri, installerUid, null);
final Message msg = mHandler.obtainMessage(INIT_COPY);
- msg.obj = new InstallParams(stageDir, true, observer, params.installFlags,
+ msg.obj = new InstallParams(stagedDir, stagedCid, true, observer, params.installFlags,
installerPackageName, verifParams, user, params.abiOverride);
mHandler.sendMessage(msg);
}
@@ -8551,10 +8554,12 @@ public class PackageManagerService extends IPackageManager.Stub {
* file, or a cluster directory. This location may be untrusted.
*/
final File originFile;
+ final String originCid;
/**
- * Flag indicating that {@link #originFile} has already been staged,
- * meaning downstream users don't need to defensively copy the contents.
+ * Flag indicating that {@link #originFile} or {@link #originCid} has
+ * already been staged, meaning downstream users don't need to
+ * defensively copy the contents.
*/
boolean originStaged;
@@ -8567,11 +8572,12 @@ public class PackageManagerService extends IPackageManager.Stub {
final String packageAbiOverride;
boolean multiArch;
- InstallParams(File originFile, boolean originStaged, IPackageInstallObserver2 observer,
- int flags, String installerPackageName, VerificationParams verificationParams,
- UserHandle user, String packageAbiOverride) {
+ InstallParams(File originFile, String originCid, boolean originStaged,
+ IPackageInstallObserver2 observer, int flags, String installerPackageName,
+ VerificationParams verificationParams, UserHandle user, String packageAbiOverride) {
super(user);
- this.originFile = Preconditions.checkNotNull(originFile);
+ this.originFile = originFile;
+ this.originCid = originCid;
this.originStaged = originStaged;
this.observer = observer;
this.flags = flags;
@@ -8582,9 +8588,8 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public String toString() {
- return "InstallParams{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + originFile + "}";
+ return "InstallParams{" + Integer.toHexString(System.identityHashCode(this))
+ + " file=" + originFile + " cid=" + originCid + "}";
}
public ManifestDigest getManifestDigest() {
@@ -8653,15 +8658,6 @@ public class PackageManagerService extends IPackageManager.Stub {
return pkgLite.recommendedInstallLocation;
}
- private long getMemoryLowThreshold() {
- final DeviceStorageMonitorInternal
- dsm = LocalServices.getService(DeviceStorageMonitorInternal.class);
- if (dsm == null) {
- return 0L;
- }
- return dsm.getMemoryLowThreshold();
- }
-
/*
* Invoke remote method to get package information and install
* location values. Override install location based on default
@@ -8679,14 +8675,9 @@ public class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
- final long lowThreshold = getMemoryLowThreshold();
- if (lowThreshold == 0L) {
- Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed");
- }
-
// Remote call to find out default install location
final String originPath = originFile.getAbsolutePath();
- pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags, lowThreshold,
+ pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags,
packageAbiOverride);
// Keep track of whether this package is a multiArch package until
// we perform a full scan of it. We need to do this because we might
@@ -8700,12 +8691,19 @@ public class PackageManagerService extends IPackageManager.Stub {
*/
if (pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
- final long size = mContainerService.calculateInstalledSize(
+ // TODO: focus freeing disk space on the target device
+ final StorageManager storage = StorageManager.from(mContext);
+ final long lowThreshold = storage.getStorageLowBytes(
+ Environment.getDataDirectory());
+
+ final long sizeBytes = mContainerService.calculateInstalledSize(
originPath, isForwardLocked(), packageAbiOverride);
- if (mInstaller.freeCache(size + lowThreshold) >= 0) {
+
+ if (mInstaller.freeCache(sizeBytes + lowThreshold) >= 0) {
pkgLite = mContainerService.getMinimalPackageInfo(originPath, flags,
- lowThreshold, packageAbiOverride);
+ packageAbiOverride);
}
+
/*
* The cache free must have deleted the file we
* downloaded to install.
@@ -9235,24 +9233,11 @@ public class PackageManagerService extends IPackageManager.Stub {
}
boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
- final long lowThreshold;
-
- final DeviceStorageMonitorInternal
- dsm = LocalServices.getService(DeviceStorageMonitorInternal.class);
- if (dsm == null) {
- Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed");
- lowThreshold = 0L;
- } else {
- if (dsm.isMemoryLow()) {
- Log.w(TAG, "Memory is reported as being too low; aborting package install");
- return false;
- }
+ final long sizeBytes = imcs.calculateInstalledSize(originFile.getAbsolutePath(),
+ isFwdLocked(), abiOverride);
- lowThreshold = dsm.getMemoryLowThreshold();
- }
-
- return imcs.checkInternalFreeStorage(originFile.getAbsolutePath(), isFwdLocked(),
- lowThreshold);
+ final StorageManager storage = StorageManager.from(mContext);
+ return (sizeBytes <= storage.getStorageBytesUntilLow(Environment.getDataDirectory()));
}
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
@@ -9264,7 +9249,7 @@ public class PackageManagerService extends IPackageManager.Stub {
resourceFile = originFile;
} else {
try {
- final File tempDir = mInstallerService.allocateSessionDir();
+ final File tempDir = mInstallerService.allocateInternalStageDirLegacy();
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
@@ -9569,12 +9554,22 @@ public class PackageManagerService extends IPackageManager.Stub {
}
void createCopyFile() {
- cid = getTempContainerId();
+ cid = mInstallerService.allocateExternalStageCidLegacy();
}
boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException {
- return imcs.checkExternalFreeStorage(originFile.getAbsolutePath(), isFwdLocked(),
+ final long sizeBytes = imcs.calculateInstalledSize(packagePath, isFwdLocked(),
abiOverride);
+
+ final File target;
+ if (isExternal()) {
+ target = Environment.getExternalStorageDirectory();
+ } else {
+ target = Environment.getDataDirectory();
+ }
+
+ final StorageManager storage = StorageManager.from(mContext);
+ return (sizeBytes <= storage.getStorageBytesUntilLow(target));
}
private final boolean isExternal() {
@@ -12653,7 +12648,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private boolean mMediaMounted = false;
- private String getEncryptKey() {
+ static String getEncryptKey() {
try {
String sdEncKey = SystemKeyStore.getInstance().retrieveKeyHexString(
SD_ENCRYPTION_KEYSTORE_NAME);
@@ -12673,30 +12668,6 @@ public class PackageManagerService extends IPackageManager.Stub {
Slog.e(TAG, "Failed to retrieve encryption keys with exception: " + ioe);
return null;
}
-
- }
-
- /* package */static String getTempContainerId() {
- int tmpIdx = 1;
- String list[] = PackageHelper.getSecureContainerList();
- if (list != null) {
- for (final String name : list) {
- // Ignore null and non-temporary container entries
- if (name == null || !name.startsWith(mTempContainerPrefix)) {
- continue;
- }
-
- String subStr = name.substring(mTempContainerPrefix.length());
- try {
- int cid = Integer.parseInt(subStr);
- if (cid >= tmpIdx) {
- tmpIdx = cid + 1;
- }
- } catch (NumberFormatException e) {
- }
- }
- }
- return mTempContainerPrefix + tmpIdx;
}
/*
@@ -12754,31 +12725,27 @@ public class PackageManagerService extends IPackageManager.Stub {
*/
private void updateExternalMediaStatusInner(boolean isMounted, boolean reportStatus,
boolean externalStorage) {
- // Collection of uids
- int uidArr[] = null;
- // Collection of stale containers
- HashSet<String> removeCids = new HashSet<String>();
- // Collection of packages on external media with valid containers.
- HashMap<AsecInstallArgs, String> processCids = new HashMap<AsecInstallArgs, String>();
- // Get list of secure containers.
- final String list[] = PackageHelper.getSecureContainerList();
- if (list == null || list.length == 0) {
- Log.i(TAG, "No secure containers on sdcard");
+ ArrayMap<AsecInstallArgs, String> processCids = new ArrayMap<>();
+ int[] uidArr = EmptyArray.INT;
+
+ final String[] list = PackageHelper.getSecureContainerList();
+ if (ArrayUtils.isEmpty(list)) {
+ Log.i(TAG, "No secure containers found");
} else {
// Process list of secure containers and categorize them
// as active or stale based on their package internal state.
- int uidList[] = new int[list.length];
- int num = 0;
+
// reader
synchronized (mPackages) {
for (String cid : list) {
+ // Leave stages untouched for now; installer service owns them
+ if (PackageInstallerService.isStageName(cid)) continue;
+
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Processing container " + cid);
String pkgName = getAsecPackageName(cid);
if (pkgName == null) {
- if (DEBUG_SD_INSTALL)
- Log.i(TAG, "Container : " + cid + " stale");
- removeCids.add(cid);
+ Slog.i(TAG, "Found stale container " + cid + " with no package name");
continue;
}
if (DEBUG_SD_INSTALL)
@@ -12786,8 +12753,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps == null) {
- Log.i(TAG, "Deleting container with no matching settings " + cid);
- removeCids.add(cid);
+ Slog.i(TAG, "Found stale container " + cid + " with no matching settings");
continue;
}
@@ -12813,35 +12779,25 @@ public class PackageManagerService extends IPackageManager.Stub {
processCids.put(args, ps.codePathString);
final int uid = ps.appId;
if (uid != -1) {
- uidList[num++] = uid;
+ uidArr = ArrayUtils.appendInt(uidArr, uid);
}
} else {
- Log.i(TAG, "Deleting stale container for " + cid);
- removeCids.add(cid);
+ Slog.i(TAG, "Found stale container " + cid + ": expected codePath="
+ + ps.codePathString);
}
}
}
- if (num > 0) {
- // Sort uid list
- Arrays.sort(uidList, 0, num);
- // Throw away duplicates
- uidArr = new int[num];
- uidArr[0] = uidList[0];
- int di = 0;
- for (int i = 1; i < num; i++) {
- if (uidList[i - 1] != uidList[i]) {
- uidArr[di++] = uidList[i];
- }
- }
- }
+ Arrays.sort(uidArr);
}
+
// Process packages with valid entries.
if (isMounted) {
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Loading packages");
- loadMediaPackages(processCids, uidArr, removeCids);
+ loadMediaPackages(processCids, uidArr);
startCleaningPackages();
+ mInstallerService.onSecureContainersAvailable();
} else {
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Unloading packages");
@@ -12849,8 +12805,8 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
- ArrayList<String> pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
+ private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
+ ArrayList<String> pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
int size = pkgList.size();
if (size > 0) {
// Send broadcasts here
@@ -12875,11 +12831,10 @@ public class PackageManagerService extends IPackageManager.Stub {
* the cid is added to list of removeCids. We currently don't delete stale
* containers.
*/
- private void loadMediaPackages(HashMap<AsecInstallArgs, String> processCids, int uidArr[],
- HashSet<String> removeCids) {
+ private void loadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int[] uidArr) {
ArrayList<String> pkgList = new ArrayList<String>();
Set<AsecInstallArgs> keys = processCids.keySet();
- boolean doGc = false;
+
for (AsecInstallArgs args : keys) {
String codePath = processCids.get(args);
if (DEBUG_SD_INSTALL)
@@ -12907,7 +12862,6 @@ public class PackageManagerService extends IPackageManager.Stub {
parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
}
- doGc = true;
synchronized (mInstallLock) {
PackageParser.Package pkg = null;
try {
@@ -12937,9 +12891,7 @@ public class PackageManagerService extends IPackageManager.Stub {
} finally {
if (retCode != PackageManager.INSTALL_SUCCEEDED) {
- // Don't destroy container here. Wait till gc clears things
- // up.
- removeCids.add(args.cid);
+ Log.w(TAG, "Container " + args.cid + " is stale, retCode=" + retCode);
}
}
}
@@ -12974,21 +12926,6 @@ public class PackageManagerService extends IPackageManager.Stub {
if (pkgList.size() > 0) {
sendResourcesChangedBroadcast(true, false, pkgList, uidArr, null);
}
- // Force gc to avoid any stale parser references that we might have.
- if (doGc) {
- Runtime.getRuntime().gc();
- }
- // List stale containers and destroy stale temporary containers.
- if (removeCids != null) {
- for (String cid : removeCids) {
- if (cid.startsWith(mTempContainerPrefix)) {
- Log.i(TAG, "Destroying stale temporary container " + cid);
- PackageHelper.destroySdDir(cid);
- } else {
- Log.w(TAG, "Container " + cid + " is stale");
- }
- }
- }
}
/*
@@ -13012,7 +12949,7 @@ public class PackageManagerService extends IPackageManager.Stub {
* that we always have to post this message if status has been requested no
* matter what.
*/
- private void unloadMediaPackages(HashMap<AsecInstallArgs, String> processCids, int uidArr[],
+ private void unloadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int uidArr[],
final boolean reportStatus) {
if (DEBUG_SD_INSTALL)
Log.i(TAG, "unloading media packages");
diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java
index e7dd82d..c7d95aa 100644
--- a/services/core/java/com/android/server/tv/TvInputHal.java
+++ b/services/core/java/com/android/server/tv/TvInputHal.java
@@ -19,12 +19,11 @@ package com.android.server.tv;
import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvStreamConfig;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Message;
-import android.view.Surface;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.view.Surface;
import java.util.LinkedList;
import java.util.Queue;
@@ -64,12 +63,12 @@ final class TvInputHal implements Handler.Callback {
int generation);
private static native void nativeClose(long ptr);
- private Object mLock = new Object();
+ private final Object mLock = new Object();
private long mPtr = 0;
private final Callback mCallback;
private final Handler mHandler;
- private SparseIntArray mStreamConfigGenerations = new SparseIntArray();
- private SparseArray<TvStreamConfig[]> mStreamConfigs = new SparseArray<>();;
+ private final SparseIntArray mStreamConfigGenerations = new SparseIntArray();
+ private final SparseArray<TvStreamConfig[]> mStreamConfigs = new SparseArray<>();
public TvInputHal(Callback callback) {
mCallback = callback;
@@ -153,7 +152,7 @@ final class TvInputHal implements Handler.Callback {
// Handler.Callback implementation
- private Queue<Message> mPendingMessageQueue = new LinkedList<Message>();
+ private final Queue<Message> mPendingMessageQueue = new LinkedList<Message>();
@Override
public boolean handleMessage(Message msg) {
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index ae9ae13..425eff3 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -20,14 +20,12 @@ import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
import android.content.Context;
-import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiHotplugEvent;
import android.hardware.hdmi.IHdmiControlService;
import android.hardware.hdmi.IHdmiDeviceEventListener;
import android.hardware.hdmi.IHdmiHotplugEventListener;
-import android.hardware.hdmi.IHdmiInputChangeListener;
import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
import android.media.AudioDevicePort;
import android.media.AudioFormat;
@@ -39,7 +37,6 @@ import android.media.AudioPort;
import android.media.AudioPortConfig;
import android.media.tv.ITvInputHardware;
import android.media.tv.ITvInputHardwareCallback;
-import android.media.tv.TvContract;
import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvInputInfo;
import android.media.tv.TvStreamConfig;
@@ -77,7 +74,6 @@ import java.util.Map;
class TvInputHardwareManager implements TvInputHal.Callback {
private static final String TAG = TvInputHardwareManager.class.getSimpleName();
- private final Context mContext;
private final Listener mListener;
private final TvInputHal mHal = new TvInputHal(this);
private final SparseArray<Connection> mConnections = new SparseArray<>();
@@ -107,7 +103,6 @@ class TvInputHardwareManager implements TvInputHal.Callback {
private final Object mLock = new Object();
public TvInputHardwareManager(Context context, Listener listener) {
- mContext = context;
mListener = listener;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mHal.init();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 112972f..aa20892 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -428,7 +428,7 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Stores for each user whether screencapture is disabled
* This array is essentially a cache for all userId for
- * {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled(null, userId)}
+ * {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled}
*/
SparseArray<Boolean> mScreenCaptureDisabled = new SparseArray<Boolean>();
@@ -2315,9 +2315,12 @@ public class WindowManagerService extends IWindowManager.Stub
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (token.appWindowToken != null) {
- Slog.i(TAG, "Non-null appWindowToken for system window of type=" + type);
- // app token should be null for any other window types.
- token.appWindowToken = null;
+ Slog.w(TAG, "Non-null appWindowToken for system window of type=" + type);
+ // It is not valid to use an app token with other system types; we will
+ // instead make a new token for it (as if null had been passed in for the token).
+ attrs.token = null;
+ token = new WindowToken(this, null, -1, false);
+ addToken = true;
}
win = new WindowState(this, session, client, token,
diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml
index 15d075c..c0898d3 100644
--- a/tests/ActivityTests/AndroidManifest.xml
+++ b/tests/ActivityTests/AndroidManifest.xml
@@ -23,6 +23,7 @@
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.MANAGE_USERS" />
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application android:label="ActivityTest">
<activity android:name="ActivityTestMain">
<intent-filter>
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index 0e063d6..9002125 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -34,6 +34,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.graphics.Bitmap;
+import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -130,6 +131,12 @@ public class ActivityTestMain extends Activity {
mSecondUser = ui.id;
}
}
+
+ /*
+ AlertDialog ad = new AlertDialog.Builder(this).setTitle("title").setMessage("message").create();
+ ad.getWindow().getAttributes().type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
+ ad.show();
+ */
}
@Override
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 6a5d06e..700afa1 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -91,7 +91,6 @@ LOCAL_C_INCLUDES += $(aaptCIncludes)
LOCAL_CFLAGS += -Wno-format-y2k
LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
-LOCAL_CFLAGS += -DAAPT_VERSION=\"$(BUILD_NUMBER)\"
ifeq (darwin,$(HOST_OS))
LOCAL_CFLAGS += -D_DARWIN_UNLIMITED_STREAMS
endif
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index fa2bacd..5d146d6 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -32,7 +32,7 @@ int doVersion(Bundle* bundle)
if (bundle->getFileSpecCount() != 0) {
printf("(ignoring extra arguments)\n");
}
- printf("Android Asset Packaging Tool, v0.2-" AAPT_VERSION "\n");
+ printf("Android Asset Packaging Tool, v0.2\n");
return 0;
}