diff options
27 files changed, 488 insertions, 167 deletions
diff --git a/api/current.txt b/api/current.txt index 302b99a8ff83..e0d895ed85cc 100644 --- a/api/current.txt +++ b/api/current.txt @@ -15,6 +15,7 @@ package android { field public static final java.lang.String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE"; field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER"; field public static final java.lang.String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL"; + field public static final java.lang.String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE"; field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS"; field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE"; field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; @@ -5674,10 +5675,9 @@ package android.app { } public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable { - ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent); + ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, android.app.RemoteAction); method public int describeContents(); - method public android.app.PendingIntent getUserAction(); - method public java.lang.CharSequence getUserActionTitle(); + method public android.app.RemoteAction getUserAction(); method public java.lang.CharSequence getUserMessage(); method public void showAsDialog(android.app.Activity); method public void showAsNotification(android.content.Context); @@ -31212,6 +31212,9 @@ package android.os.storage { } public class StorageManager { + method public void allocateBytes(java.io.File, long, int) throws java.io.IOException; + method public void allocateBytes(java.io.FileDescriptor, long, int) throws java.io.IOException; + method public long getAllocatableBytes(java.io.File, int) throws java.io.IOException; method public long getCacheQuotaBytes(); method public long getCacheSizeBytes(); method public long getExternalCacheQuotaBytes(); @@ -31230,6 +31233,7 @@ package android.os.storage { method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException; method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE"; + field public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1 } public final class StorageVolume implements android.os.Parcelable { diff --git a/api/system-current.txt b/api/system-current.txt index 486944008408..a5f30819e353 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -23,6 +23,7 @@ package android { field public static final java.lang.String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE"; field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER"; field public static final java.lang.String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL"; + field public static final java.lang.String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE"; field public static final java.lang.String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"; field public static final java.lang.String BACKUP = "android.permission.BACKUP"; field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS"; @@ -5870,10 +5871,9 @@ package android.app { } public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable { - ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent); + ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, android.app.RemoteAction); method public int describeContents(); - method public android.app.PendingIntent getUserAction(); - method public java.lang.CharSequence getUserActionTitle(); + method public android.app.RemoteAction getUserAction(); method public java.lang.CharSequence getUserMessage(); method public void showAsDialog(android.app.Activity); method public void showAsNotification(android.content.Context); @@ -34082,6 +34082,9 @@ package android.os.storage { } public class StorageManager { + method public void allocateBytes(java.io.File, long, int) throws java.io.IOException; + method public void allocateBytes(java.io.FileDescriptor, long, int) throws java.io.IOException; + method public long getAllocatableBytes(java.io.File, int) throws java.io.IOException; method public long getCacheQuotaBytes(); method public long getCacheSizeBytes(); method public long getExternalCacheQuotaBytes(); @@ -34100,6 +34103,7 @@ package android.os.storage { method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException; method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE"; + field public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1 } public final class StorageVolume implements android.os.Parcelable { diff --git a/api/test-current.txt b/api/test-current.txt index 7c06b9e67f2c..a983cf540b64 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -15,6 +15,7 @@ package android { field public static final java.lang.String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE"; field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER"; field public static final java.lang.String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL"; + field public static final java.lang.String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE"; field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS"; field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE"; field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; @@ -5685,10 +5686,9 @@ package android.app { } public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable { - ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent); + ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, android.app.RemoteAction); method public int describeContents(); - method public android.app.PendingIntent getUserAction(); - method public java.lang.CharSequence getUserActionTitle(); + method public android.app.RemoteAction getUserAction(); method public java.lang.CharSequence getUserMessage(); method public void showAsDialog(android.app.Activity); method public void showAsNotification(android.content.Context); @@ -31327,6 +31327,9 @@ package android.os.storage { } public class StorageManager { + method public void allocateBytes(java.io.File, long, int) throws java.io.IOException; + method public void allocateBytes(java.io.FileDescriptor, long, int) throws java.io.IOException; + method public long getAllocatableBytes(java.io.File, int) throws java.io.IOException; method public long getCacheQuotaBytes(); method public long getCacheSizeBytes(); method public long getExternalCacheQuotaBytes(); @@ -31345,6 +31348,7 @@ package android.os.storage { method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException; method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE"; + field public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1 } public final class StorageVolume implements android.os.Parcelable { diff --git a/core/java/android/app/RecoverableSecurityException.java b/core/java/android/app/RecoverableSecurityException.java index 1f015a607be8..540d1cda1edd 100644 --- a/core/java/android/app/RecoverableSecurityException.java +++ b/core/java/android/app/RecoverableSecurityException.java @@ -17,6 +17,8 @@ package android.app; import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -40,13 +42,12 @@ public final class RecoverableSecurityException extends SecurityException implem private static final String TAG = "RecoverableSecurityException"; private final CharSequence mUserMessage; - private final CharSequence mUserActionTitle; - private final PendingIntent mUserAction; + private final RemoteAction mUserAction; /** {@hide} */ public RecoverableSecurityException(Parcel in) { - this(new SecurityException(in.readString()), in.readCharSequence(), in.readCharSequence(), - PendingIntent.CREATOR.createFromParcel(in)); + this(new SecurityException(in.readString()), in.readCharSequence(), + RemoteAction.CREATOR.createFromParcel(in)); } /** @@ -56,26 +57,35 @@ public final class RecoverableSecurityException extends SecurityException implem * audiences. * @param userMessage short message describing the issue for end user * audiences, which may be shown in a notification or dialog. - * This should be less than 64 characters. For example: <em>PIN - * required to access Document.pdf</em> - * @param userActionTitle short title describing the primary action. This - * should be less than 24 characters. For example: <em>Enter - * PIN</em> - * @param userAction primary action that will initiate the recovery. This - * must launch an activity that is expected to set + * This should be localized and less than 64 characters. For + * example: <em>PIN required to access Document.pdf</em> + * @param userAction primary action that will initiate the recovery. The + * title should be localized and less than 24 characters. For + * example: <em>Enter PIN</em>. This action must launch an + * activity that is expected to set * {@link Activity#setResult(int)} before finishing to * communicate the final status of the recovery. For example, * apps that observe {@link Activity#RESULT_OK} may choose to * immediately retry their operation. */ public RecoverableSecurityException(Throwable cause, CharSequence userMessage, - CharSequence userActionTitle, PendingIntent userAction) { + RemoteAction userAction) { super(cause.getMessage()); mUserMessage = Preconditions.checkNotNull(userMessage); - mUserActionTitle = Preconditions.checkNotNull(userActionTitle); mUserAction = Preconditions.checkNotNull(userAction); } + /** {@hide} */ + @Deprecated + public RecoverableSecurityException(Throwable cause, CharSequence userMessage, + CharSequence userActionTitle, PendingIntent userAction) { + this(cause, userMessage, + new RemoteAction( + Icon.createWithResource("android", + com.android.internal.R.drawable.ic_restart), + userActionTitle, userActionTitle, userAction)); + } + /** * Return short message describing the issue for end user audiences, which * may be shown in a notification or dialog. @@ -85,16 +95,9 @@ public final class RecoverableSecurityException extends SecurityException implem } /** - * Return short title describing the primary action. - */ - public CharSequence getUserActionTitle() { - return mUserActionTitle; - } - - /** * Return primary action that will initiate the recovery. */ - public PendingIntent getUserAction() { + public RemoteAction getUserAction() { return mUserAction; } @@ -113,15 +116,21 @@ public final class RecoverableSecurityException extends SecurityException implem * remote UID; notifications from older exceptions will always be replaced. */ public void showAsNotification(Context context) { - final Notification.Builder builder = new Notification.Builder(context) + final NotificationManager nm = context.getSystemService(NotificationManager.class); + + // Create a channel per-sender, since we don't want one poorly behaved + // remote app to cause all of our notifications to be blocked + final String tag = TAG + "_" + mUserAction.getActionIntent().getCreatorUid(); + nm.createNotificationChannel(new NotificationChannel(tag, TAG, + NotificationManager.IMPORTANCE_DEFAULT)); + + final Notification.Builder builder = new Notification.Builder(context, tag) .setSmallIcon(com.android.internal.R.drawable.ic_print_error) - .setContentTitle(mUserActionTitle) + .setContentTitle(mUserAction.getTitle()) .setContentText(mUserMessage) - .setContentIntent(mUserAction) + .setContentIntent(mUserAction.getActionIntent()) .setCategory(Notification.CATEGORY_ERROR); - - final NotificationManager nm = context.getSystemService(NotificationManager.class); - nm.notify(TAG, mUserAction.getCreatorUid(), builder.build()); + nm.notify(tag, 0, builder.build()); } /** @@ -144,7 +153,7 @@ public final class RecoverableSecurityException extends SecurityException implem args.putParcelable(TAG, this); dialog.setArguments(args); - final String tag = TAG + "_" + mUserAction.getCreatorUid(); + final String tag = TAG + "_" + mUserAction.getActionIntent().getCreatorUid(); final FragmentManager fm = activity.getFragmentManager(); final FragmentTransaction ft = fm.beginTransaction(); final Fragment old = fm.findFragmentByTag(tag); @@ -162,9 +171,9 @@ public final class RecoverableSecurityException extends SecurityException implem final RecoverableSecurityException e = getArguments().getParcelable(TAG); return new AlertDialog.Builder(getActivity()) .setMessage(e.mUserMessage) - .setPositiveButton(e.mUserActionTitle, (dialog, which) -> { + .setPositiveButton(e.mUserAction.getTitle(), (dialog, which) -> { try { - e.mUserAction.send(); + e.mUserAction.getActionIntent().send(); } catch (PendingIntent.CanceledException ignored) { } }) @@ -182,7 +191,6 @@ public final class RecoverableSecurityException extends SecurityException implem public void writeToParcel(Parcel dest, int flags) { dest.writeString(getMessage()); dest.writeCharSequence(mUserMessage); - dest.writeCharSequence(mUserActionTitle); mUserAction.writeToParcel(dest, flags); } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 9a8eff098c1e..48c52d5fc4e8 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -824,6 +824,7 @@ public class ActivityInfo extends ComponentInfo * WindowManager.LayoutParams.softInputMode}. If 0 (unspecified), * the mode from the theme will be used. */ + @android.view.WindowManager.LayoutParams.SoftInputModeFlags public int softInputMode; /** diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 167d5a09a2dc..bc407504a20d 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -61,7 +61,6 @@ class IInputMethodWrapper extends IInputMethod.Stub private static final int DO_SET_INPUT_CONTEXT = 20; private static final int DO_UNSET_INPUT_CONTEXT = 30; private static final int DO_START_INPUT = 32; - private static final int DO_RESTART_INPUT = 34; private static final int DO_CREATE_SESSION = 40; private static final int DO_SET_SESSION_ENABLED = 45; private static final int DO_REVOKE_SESSION = 50; @@ -164,26 +163,19 @@ class IInputMethodWrapper extends IInputMethod.Stub inputMethod.unbindInput(); return; case DO_START_INPUT: { - SomeArgs args = (SomeArgs)msg.obj; - int missingMethods = msg.arg1; - IInputContext inputContext = (IInputContext)args.arg1; - InputConnection ic = inputContext != null - ? new InputConnectionWrapper(mTarget, inputContext, missingMethods) : null; - EditorInfo info = (EditorInfo)args.arg2; - info.makeCompatible(mTargetSdkVersion); - inputMethod.startInput(ic, info); - args.recycle(); - return; - } - case DO_RESTART_INPUT: { - SomeArgs args = (SomeArgs)msg.obj; - int missingMethods = msg.arg1; - IInputContext inputContext = (IInputContext)args.arg1; - InputConnection ic = inputContext != null + final SomeArgs args = (SomeArgs) msg.obj; + final int missingMethods = msg.arg1; + final boolean restarting = msg.arg2 != 0; + final IInputContext inputContext = (IInputContext) args.arg1; + final EditorInfo info = (EditorInfo) args.arg2; + final InputConnection ic = inputContext != null ? new InputConnectionWrapper(mTarget, inputContext, missingMethods) : null; - EditorInfo info = (EditorInfo)args.arg2; info.makeCompatible(mTargetSdkVersion); - inputMethod.restartInput(ic, info); + if (restarting) { + inputMethod.restartInput(ic, info); + } else { + inputMethod.startInput(ic, info); + } args.recycle(); return; } @@ -265,17 +257,9 @@ class IInputMethodWrapper extends IInputMethod.Stub @Override public void startInput(IInputContext inputContext, @InputConnectionInspector.MissingMethodFlags final int missingMethods, - EditorInfo attribute) { - mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_START_INPUT, - missingMethods, inputContext, attribute)); - } - - @Override - public void restartInput(IInputContext inputContext, - @InputConnectionInspector.MissingMethodFlags final int missingMethods, - EditorInfo attribute) { - mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_RESTART_INPUT, - missingMethods, inputContext, attribute)); + EditorInfo attribute, boolean restarting) { + mCaller.executeOrSendMessage(mCaller.obtainMessageIIOO(DO_START_INPUT, + missingMethods, restarting ? 1 : 0, inputContext, attribute)); } @Override diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl index 35a266b7ab41..9e35bf68d78c 100644 --- a/core/java/android/os/storage/IStorageManager.aidl +++ b/core/java/android/os/storage/IStorageManager.aidl @@ -293,4 +293,6 @@ interface IStorageManager { ParcelFileDescriptor openProxyFileDescriptor(int mountPointId, int fileId, int mode) = 74; long getCacheQuotaBytes(String volumeUuid, int uid) = 75; long getCacheSizeBytes(String volumeUuid, int uid) = 76; + long getAllocatableBytes(String path, int flags) = 77; + void allocateBytes(String path, long bytes, int flags) = 78; } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 626d6f4698ca..2a3c03d1f84f 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -18,8 +18,10 @@ package android.os.storage; import static android.net.TrafficStats.MB_IN_BYTES; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.app.ActivityThread; import android.content.ContentResolver; @@ -34,6 +36,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; +import android.os.ParcelableException; import android.os.ProxyFileDescriptorCallback; import android.os.RemoteException; import android.os.ServiceManager; @@ -60,10 +63,13 @@ import com.android.internal.util.Preconditions; import java.io.BufferedReader; import java.io.File; +import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -1424,10 +1430,7 @@ public class StorageManager { * as a single unit. * </p> * - * @see #getCacheQuotaBytes() * @see #getCacheSizeBytes() - * @see #getExternalCacheQuotaBytes() - * @see #getExternalCacheSizeBytes() */ public long getCacheQuotaBytes() { try { @@ -1453,9 +1456,6 @@ public class StorageManager { * </p> * * @see #getCacheQuotaBytes() - * @see #getCacheSizeBytes() - * @see #getExternalCacheQuotaBytes() - * @see #getExternalCacheSizeBytes() */ public long getCacheSizeBytes() { try { @@ -1520,6 +1520,139 @@ public class StorageManager { } } + /** + * Flag indicating that a disk space allocation request should operate in an + * aggressive mode. This flag should only be rarely used in situations that + * are critical to system health or security. + * <p> + * When set, the system is more aggressive about the data that it considers + * for possible deletion when allocating disk space. + * <p class="note"> + * Note: your app must hold the + * {@link android.Manifest.permission#ALLOCATE_AGGRESSIVE} permission for + * this flag to take effect. + * </p> + * + * @see #getAllocatableBytes(File, int) + * @see #allocateBytes(File, long, int) + * @see #allocateBytes(FileDescriptor, long, int) + */ + @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) + public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; + + /** @hide */ + @IntDef(flag = true, value = { + FLAG_ALLOCATE_AGGRESSIVE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AllocateFlags {} + + /** + * Return the maximum number of new bytes that your app can allocate for + * itself using {@link #allocateBytes(File, long, int)} at the given path. + * This value is typically larger than {@link File#getUsableSpace()}, since + * the system may automatically delete cached files to satisfy your request. + * <p> + * This method is best used as a pre-flight check, such as deciding if there + * is enough space to store an entire music album before you allocate space + * for each audio file in the album. Attempts to allocate disk space beyond + * this value will fail. + * <p class="note"> + * Note: if your app uses the {@code android:sharedUserId} manifest feature, + * then allocatable space for all packages in your shared UID is tracked + * together as a single unit. + * </p> + * + * @param file the directory where you're considering allocating disk space, + * since allocatable space can vary widely depending on the + * underlying storage device. + * @param flags to apply to the request. + * @return the maximum number of new bytes that the calling app can allocate + * using {@link #allocateBytes(File, long, int)}. + */ + public long getAllocatableBytes(File file, @AllocateFlags int flags) throws IOException { + try { + return mStorageManager.getAllocatableBytes(file.getAbsolutePath(), flags); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + throw new RuntimeException(e); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Allocate the requested number of bytes for your application to use at the + * given path. This will cause the system to delete any cached files + * necessary to satisfy your request. + * <p> + * Attempts to allocate disk space beyond the value returned by + * {@link #getAllocatableBytes(File, int)} will fail. + * <p> + * Since multiple apps can be running simultaneously, this method may be + * subject to race conditions. If possible, consider using + * {@link #allocateBytes(FileDescriptor, long, int)} which will guarantee + * that bytes are allocated to an opened file. + * + * @param file the directory where you'd like to allocate disk space. + * @param bytes the number of bytes to allocate. + * @param flags to apply to the request. + * @see #getAllocatableBytes(File, int) + */ + public void allocateBytes(File file, long bytes, @AllocateFlags int flags) throws IOException { + try { + mStorageManager.allocateBytes(file.getAbsolutePath(), bytes, flags); + } catch (ParcelableException e) { + e.maybeRethrow(IOException.class); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Allocate the requested number of bytes for your application to use at the + * given path. This will cause the system to delete any cached files + * necessary to satisfy your request. + * <p> + * Attempts to allocate disk space beyond the value returned by + * {@link #getAllocatableBytes(File, int)} will fail. + * <p> + * This method guarantees that bytes are allocated to the opened file, + * otherwise it will throw if fast allocation not possible. Fast allocation + * is typically only supported in private app data directories, and on + * shared/external storage devices which are emulated. + * + * @param fd the directory where you'd like to allocate disk space. + * @param bytes the number of bytes to allocate. + * @param flags to apply to the request. + * @see #getAllocatableBytes(File, int) + * @see Environment#isExternalStorageEmulated(File) + */ + public void allocateBytes(FileDescriptor fd, long bytes, @AllocateFlags int flags) + throws IOException { + final File file; + try { + file = new File(Os.readlink("/proc/self/fd/" + fd.getInt$())); + } catch (ErrnoException e) { + throw e.rethrowAsIOException(); + } + for (int i = 0; i < 3; i++) { + allocateBytes(file, bytes, flags); + + try { + Os.posix_fallocate(fd, 0, bytes); + } catch (ErrnoException e) { + if (e.errno == OsConstants.ENOSPC) { + Log.w(TAG, "Odd, not enough space; let's try again?"); + continue; + } + throw e.rethrowAsIOException(); + } + } + throw new IOException( + "Well this is embarassing; we can't allocate " + bytes + " for " + file); + } + private static final String XATTR_ATOMIC = "user.atomic"; private static final String XATTR_TOMBSTONE = "user.tombstone"; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index bdc3e7f468c9..cc4c5c154bcf 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -73,6 +73,7 @@ import android.util.TypedValue; import android.view.Surface.OutOfResourcesException; import android.view.View.AttachInfo; import android.view.View.MeasureSpec; +import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; @@ -334,6 +335,7 @@ public final class ViewRootImpl implements ViewParent, final Configuration mPendingConfiguration = new Configuration(); boolean mScrollMayChange; + @SoftInputModeFlags int mSoftInputMode; WeakReference<View> mLastScrolledFocus; int mScrollY; diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 5a640faf08d3..051f56f7152f 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.app.KeyguardManager; @@ -30,6 +31,8 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Objects; @@ -1540,24 +1543,45 @@ public interface WindowManager extends ViewManager { public static final int SOFT_INPUT_IS_FORWARD_NAVIGATION = 0x100; /** + * An internal annotation for flags that can be specified to {@link #softInputMode}. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = { + SOFT_INPUT_STATE_UNSPECIFIED, + SOFT_INPUT_STATE_UNCHANGED, + SOFT_INPUT_STATE_HIDDEN, + SOFT_INPUT_STATE_ALWAYS_HIDDEN, + SOFT_INPUT_STATE_VISIBLE, + SOFT_INPUT_STATE_ALWAYS_VISIBLE, + SOFT_INPUT_ADJUST_UNSPECIFIED, + SOFT_INPUT_ADJUST_RESIZE, + SOFT_INPUT_ADJUST_PAN, + SOFT_INPUT_ADJUST_NOTHING, + SOFT_INPUT_IS_FORWARD_NAVIGATION, + }) + public @interface SoftInputModeFlags {} + + /** * Desired operating mode for any soft input area. May be any combination * of: * * <ul> * <li> One of the visibility states * {@link #SOFT_INPUT_STATE_UNSPECIFIED}, {@link #SOFT_INPUT_STATE_UNCHANGED}, - * {@link #SOFT_INPUT_STATE_HIDDEN}, {@link #SOFT_INPUT_STATE_ALWAYS_VISIBLE}, or - * {@link #SOFT_INPUT_STATE_VISIBLE}. + * {@link #SOFT_INPUT_STATE_HIDDEN}, {@link #SOFT_INPUT_STATE_ALWAYS_HIDDEN}, + * {@link #SOFT_INPUT_STATE_VISIBLE}, or {@link #SOFT_INPUT_STATE_ALWAYS_VISIBLE}. * <li> One of the adjustment options - * {@link #SOFT_INPUT_ADJUST_UNSPECIFIED}, - * {@link #SOFT_INPUT_ADJUST_RESIZE}, or - * {@link #SOFT_INPUT_ADJUST_PAN}. + * {@link #SOFT_INPUT_ADJUST_UNSPECIFIED}, {@link #SOFT_INPUT_ADJUST_RESIZE}, + * {@link #SOFT_INPUT_ADJUST_PAN}, or {@link #SOFT_INPUT_ADJUST_NOTHING}. * </ul> * * * <p>This flag can be controlled in your theme through the * {@link android.R.attr#windowSoftInputMode} attribute.</p> */ + @SoftInputModeFlags public int softInputMode; /** diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 13abe7c6471d..1f76d28fab05 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -47,6 +47,7 @@ import android.view.InputEventSender; import android.view.KeyEvent; import android.view.View; import android.view.ViewRootImpl; +import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.os.SomeArgs; @@ -1490,12 +1491,12 @@ public final class InputMethodManager { * Called by ViewAncestor when its window gets input focus. * @hide */ - public void onPostWindowFocus(View rootView, View focusedView, int softInputMode, - boolean first, int windowFlags) { + public void onPostWindowFocus(View rootView, View focusedView, + @SoftInputModeFlags int softInputMode, boolean first, int windowFlags) { boolean forceNewFocus = false; synchronized (mH) { if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView - + " softInputMode=" + softInputMode + + " softInputMode=" + InputMethodClient.softInputModeToString(softInputMode) + " first=" + first + " flags=#" + Integer.toHexString(windowFlags)); if (mHasBeenInactive) { diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 989927e0c22d..1436bdb3cbd9 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -54,6 +54,7 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.ViewTreeObserver.OnScrollChangedListener; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; +import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import com.android.internal.R; @@ -162,6 +163,7 @@ public class PopupWindow { private boolean mFocusable; private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE; + @SoftInputModeFlags private int mSoftInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED; private boolean mTouchable = true; private boolean mOutsideTouchable = false; @@ -724,7 +726,7 @@ public class PopupWindow { * @see android.view.WindowManager.LayoutParams#softInputMode * @see #getSoftInputMode() */ - public void setSoftInputMode(int mode) { + public void setSoftInputMode(@SoftInputModeFlags int mode) { mSoftInputMode = mode; } @@ -734,6 +736,7 @@ public class PopupWindow { * @see #setSoftInputMode(int) * @see android.view.WindowManager.LayoutParams#softInputMode */ + @SoftInputModeFlags public int getSoftInputMode() { return mSoftInputMode; } diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl index 6ab1ec797a22..9870612dfac5 100644 --- a/core/java/com/android/internal/view/IInputMethod.aidl +++ b/core/java/com/android/internal/view/IInputMethod.aidl @@ -38,9 +38,8 @@ oneway interface IInputMethod { void unbindInput(); - void startInput(in IInputContext inputContext, int missingMethods, in EditorInfo attribute); - - void restartInput(in IInputContext inputContext, int missingMethods, in EditorInfo attribute); + void startInput(in IInputContext inputContext, int missingMethods, in EditorInfo attribute, + boolean restarting); void createSession(in InputChannel channel, IInputSessionCallback callback); diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 2279a6706524..437764562b83 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -58,8 +58,8 @@ interface IInputMethodManager { InputBindResult startInputOrWindowGainedFocus( /* @InputMethodClient.StartInputReason */ int startInputReason, in IInputMethodClient client, in IBinder windowToken, int controlFlags, - int softInputMode, int windowFlags, in EditorInfo attribute, - IInputContext inputContext, + /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode, + int windowFlags, in EditorInfo attribute, IInputContext inputContext, /* @InputConnectionInspector.MissingMethodFlags */ int missingMethodFlags); void showInputMethodPickerFromClient(in IInputMethodClient client, diff --git a/core/java/com/android/internal/view/InputMethodClient.java b/core/java/com/android/internal/view/InputMethodClient.java index cea030a3d489..bbd33a258e18 100644 --- a/core/java/com/android/internal/view/InputMethodClient.java +++ b/core/java/com/android/internal/view/InputMethodClient.java @@ -17,6 +17,8 @@ package com.android.internal.view; import android.annotation.IntDef; +import android.view.WindowManager.LayoutParams; +import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import java.lang.annotation.Retention; @@ -103,4 +105,65 @@ public final class InputMethodClient { return "Unknown=" + reason; } } + + public static String softInputModeToString(@SoftInputModeFlags final int softInputMode) { + final StringBuilder sb = new StringBuilder(); + final int state = softInputMode & LayoutParams.SOFT_INPUT_MASK_STATE; + final int adjust = softInputMode & LayoutParams.SOFT_INPUT_MASK_ADJUST; + final boolean isForwardNav = + (softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0; + + switch (state) { + case LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: + sb.append("STATE_UNSPECIFIED"); + break; + case LayoutParams.SOFT_INPUT_STATE_UNCHANGED: + sb.append("STATE_UNCHANGED"); + break; + case LayoutParams.SOFT_INPUT_STATE_HIDDEN: + sb.append("STATE_HIDDEN"); + break; + case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN: + sb.append("STATE_ALWAYS_HIDDEN"); + break; + case LayoutParams.SOFT_INPUT_STATE_VISIBLE: + sb.append("STATE_VISIBLE"); + break; + case LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: + sb.append("STATE_ALWAYS_VISIBLE"); + break; + default: + sb.append("STATE_UNKNOWN("); + sb.append(state); + sb.append(")"); + break; + } + + switch (adjust) { + case LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED: + sb.append("|ADJUST_UNSPECIFIED"); + break; + case LayoutParams.SOFT_INPUT_ADJUST_RESIZE: + sb.append("|ADJUST_RESIZE"); + break; + case LayoutParams.SOFT_INPUT_ADJUST_PAN: + sb.append("|ADJUST_PAN"); + break; + case LayoutParams.SOFT_INPUT_ADJUST_NOTHING: + sb.append("|ADJUST_NOTHING"); + break; + default: + sb.append("|ADJUST_UNKNOWN("); + sb.append(adjust); + sb.append(")"); + break; + } + + if (isForwardNav) { + // This is a special bit that is set by the system only during the window navigation. + sb.append("|IS_FORWARD_NAVIGATION"); + } + + return sb.toString(); + } } diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java index 2a5957ce0ca2..261fa4327494 100644 --- a/core/java/com/android/internal/widget/SwipeDismissLayout.java +++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java @@ -43,7 +43,8 @@ import android.widget.FrameLayout; public class SwipeDismissLayout extends FrameLayout { private static final String TAG = "SwipeDismissLayout"; - private static final float DISMISS_MIN_DRAG_WIDTH_RATIO = .33f; + private static final float MAX_DIST_THRESHOLD = .33f; + private static final float MIN_DIST_THRESHOLD = .1f; public interface OnDismissedListener { void onDismissed(SwipeDismissLayout layout); @@ -73,6 +74,7 @@ public class SwipeDismissLayout extends FrameLayout { private int mActiveTouchId; private float mDownX; private float mDownY; + private float mLastX; private boolean mSwiping; private boolean mDismissed; private boolean mDiscardIntercept; @@ -105,7 +107,6 @@ public class SwipeDismissLayout extends FrameLayout { }; private IntentFilter mScreenOffFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF); - private float mLastX; private boolean mDismissable = true; @@ -174,7 +175,7 @@ public class SwipeDismissLayout extends FrameLayout { mDownX = ev.getRawX(); mDownY = ev.getRawY(); mActiveTouchId = ev.getPointerId(0); - mVelocityTracker = VelocityTracker.obtain(); + mVelocityTracker = VelocityTracker.obtain("int1"); mVelocityTracker.addMovement(ev); break; @@ -238,7 +239,10 @@ public class SwipeDismissLayout extends FrameLayout { updateDismiss(ev); if (mDismissed) { mDismissAnimator.animateDismissal(ev.getRawX() - mDownX); - } else if (mSwiping) { + } else if (mSwiping + // Only trigger animation if we had a MOVE event that would shift the + // underlying view, otherwise the animation would be janky. + && mLastX != Integer.MIN_VALUE) { mDismissAnimator.animateRecovery(ev.getRawX() - mDownX); } resetMembers(); @@ -298,6 +302,7 @@ public class SwipeDismissLayout extends FrameLayout { mVelocityTracker = null; mTranslationX = 0; mDownX = 0; + mLastX = Integer.MIN_VALUE; mDownY = 0; mSwiping = false; mDismissed = false; @@ -329,19 +334,32 @@ public class SwipeDismissLayout extends FrameLayout { private void updateDismiss(MotionEvent ev) { float deltaX = ev.getRawX() - mDownX; - mVelocityTracker.addMovement(ev); + // Don't add the motion event as an UP event would clear the velocity tracker mVelocityTracker.computeCurrentVelocity(1000); + float xVelocity = mVelocityTracker.getXVelocity(); + if (mLastX == Integer.MIN_VALUE) { + // If there's no changes to mLastX, we have only one point of data, and therefore no + // velocity. Estimate velocity from just the up and down event in that case. + xVelocity = deltaX / ((ev.getEventTime() - ev.getDownTime()) / 1000); + } if (!mDismissed) { - if ((deltaX > (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) && - ev.getRawX() >= mLastX) - || mVelocityTracker.getXVelocity() >= mMinFlingVelocity) { + // Adjust the distance threshold linearly between the min and max threshold based on the + // x-velocity scaled with the the fling threshold speed + float distanceThreshold = getWidth() * Math.max( + Math.min((MIN_DIST_THRESHOLD - MAX_DIST_THRESHOLD) + * xVelocity / mMinFlingVelocity // scale x-velocity with fling velocity + + MAX_DIST_THRESHOLD, // offset to start at max threshold + MAX_DIST_THRESHOLD), // cap at max threshold + MIN_DIST_THRESHOLD); // bottom out at min threshold + if ((deltaX > distanceThreshold && ev.getRawX() >= mLastX) + || xVelocity >= mMinFlingVelocity) { mDismissed = true; } } // Check if the user tried to undo this. if (mDismissed && mSwiping) { // Check if the user's finger is actually flinging back to left - if (mVelocityTracker.getXVelocity() < -mMinFlingVelocity) { + if (xVelocity < -mMinFlingVelocity) { mDismissed = false; } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5b5e61e6ebdc..cd02fbbedb7e 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1650,6 +1650,12 @@ <permission android:name="android.permission.CACHE_CONTENT" android:protectionLevel="signature" /> + <!-- Allows an application to aggressively allocate disk space. + <p>Not for use by third-party applications. + --> + <permission android:name="android.permission.ALLOCATE_AGGRESSIVE" + android:protectionLevel="signature|privileged" /> + <!-- ================================== --> <!-- Permissions for screenlock --> <!-- ================================== --> diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java index ff934ef18677..4254a0ba200a 100644 --- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java +++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java @@ -217,9 +217,6 @@ public class WallpaperBackupAgent extends BackupAgent { final int sysWhich = FLAG_SYSTEM | (lockImageStage.exists() ? 0 : FLAG_LOCK); try { - // First off, revert to the factory state - mWm.clear(FLAG_SYSTEM | FLAG_LOCK); - // It is valid for the imagery to be absent; it means that we were not permitted // to back up the original image on the source device, or there was no user-supplied // wallpaper image present. @@ -233,6 +230,11 @@ public class WallpaperBackupAgent extends BackupAgent { Slog.i(TAG, "Using wallpaper service " + wpService); } mWm.setWallpaperComponent(wpService, UserHandle.USER_SYSTEM); + if (!lockImageStage.exists()) { + // We have a live wallpaper and no static lock image, + // allow live wallpaper to show "through" on lock screen. + mWm.clear(FLAG_LOCK); + } } else { if (DEBUG) { Slog.v(TAG, "Can't use wallpaper service " + wpService); diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 2b3d8d4e1fb1..b7a1f8b34277 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -175,7 +175,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_CREATE_SESSION = 1050; static final int MSG_START_INPUT = 2000; - static final int MSG_RESTART_INPUT = 2010; static final int MSG_UNBIND_CLIENT = 3000; static final int MSG_BIND_CLIENT = 3010; @@ -348,6 +347,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub IBinder mCurFocusedWindow; /** + * {@link WindowManager.LayoutParams#softInputMode} of {@link #mCurFocusedWindow}. + * + * @see #mCurFocusedWindow + */ + int mCurFocusedWindowSoftInputMode; + + /** * The client by which {@link #mCurFocusedWindow} was reported. Used only for debugging. */ ClientState mCurFocusedWindowClient; @@ -1340,15 +1346,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mBoundToMethod = true; } final SessionState session = mCurClient.curSession; - if (initial) { - executeOrSendMessage(session.method, mCaller.obtainMessageIOOO( - MSG_START_INPUT, mCurInputContextMissingMethods, session, mCurInputContext, - mCurAttribute)); - } else { - executeOrSendMessage(session.method, mCaller.obtainMessageIOOO( - MSG_RESTART_INPUT, mCurInputContextMissingMethods, session, mCurInputContext, - mCurAttribute)); - } + executeOrSendMessage(session.method, mCaller.obtainMessageIIOOO( + MSG_START_INPUT, mCurInputContextMissingMethods, initial ? 0 : 1 /* restarting */, + session, mCurInputContext, mCurAttribute)); if (mShowRequested) { if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); showCurrentInputLocked(getAppShowFlags(), null); @@ -2271,7 +2271,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private InputBindResult windowGainedFocus( /* @InputMethodClient.StartInputReason */ final int startInputReason, - IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode, + IInputMethodClient client, IBinder windowToken, int controlFlags, + /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode, int windowFlags, EditorInfo attribute, IInputContext inputContext, /* @InputConnectionInspector.missingMethods */ final int missingMethods) { // Needs to check the validity before clearing calling identity @@ -2288,7 +2289,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + InputConnectionInspector.getMissingMethodFlagsAsString(missingMethods) + " attribute=" + attribute + " controlFlags=#" + Integer.toHexString(controlFlags) - + " softInputMode=#" + Integer.toHexString(softInputMode) + + " softInputMode=" + InputMethodClient.softInputModeToString(softInputMode) + " windowFlags=#" + Integer.toHexString(windowFlags)); ClientState cs = mClients.get(client.asBinder()); @@ -2333,6 +2334,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return null; } mCurFocusedWindow = windowToken; + mCurFocusedWindowSoftInputMode = softInputMode; mCurFocusedWindowClient = cs; // Should we auto-show the IME even if the caller has not @@ -2896,26 +2898,15 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // --------------------------------------------------------- case MSG_START_INPUT: { - int missingMethods = msg.arg1; - args = (SomeArgs) msg.obj; - try { - SessionState session = (SessionState) args.arg1; - setEnabledSessionInMainThread(session); - session.method.startInput((IInputContext) args.arg2, missingMethods, - (EditorInfo) args.arg3); - } catch (RemoteException e) { - } - args.recycle(); - return true; - } - case MSG_RESTART_INPUT: { - int missingMethods = msg.arg1; + final int missingMethods = msg.arg1; + final boolean restarting = msg.arg2 != 0; args = (SomeArgs) msg.obj; + final SessionState session = (SessionState) args.arg1; + final IInputContext inputContext = (IInputContext) args.arg2; + final EditorInfo editorInfo = (EditorInfo) args.arg3; try { - SessionState session = (SessionState) args.arg1; setEnabledSessionInMainThread(session); - session.method.restartInput((IInputContext) args.arg2, missingMethods, - (EditorInfo) args.arg3); + session.method.startInput(inputContext, missingMethods, editorInfo, restarting); } catch (RemoteException e) { } args.recycle(); @@ -4087,9 +4078,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub p.println(" mCurMethodId=" + mCurMethodId); client = mCurClient; p.println(" mCurClient=" + client + " mCurSeq=" + mCurSeq); - p.println(" mCurFocusedWindow=" + mCurFocusedWindow); + p.println(" mCurFocusedWindow=" + mCurFocusedWindow + + " softInputMode=" + + InputMethodClient.softInputModeToString(mCurFocusedWindowSoftInputMode) + + " client=" + mCurFocusedWindowClient); focusedWindowClient = mCurFocusedWindowClient; - p.println(" mCurFocusedWindowClient=" + focusedWindowClient); p.println(" mCurId=" + mCurId + " mHaveConnect=" + mHaveConnection + " mBoundToMethod=" + mBoundToMethod); p.println(" mCurToken=" + mCurToken); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index c07add0267cb..32b7e4d1b5b4 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -59,6 +59,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; +import android.os.ParcelableException; import android.os.PowerManager; import android.os.Process; import android.os.RemoteCallbackList; @@ -3291,6 +3292,18 @@ class StorageManagerService extends IStorageManager.Stub } } + @Override + public long getAllocatableBytes(String path, int flags) { + return new File(path).getUsableSpace(); + } + + @Override + public void allocateBytes(String path, long bytes, int flags) { + if (bytes > new File(path).getUsableSpace()) { + throw new ParcelableException(new IOException("Not enough usable space")); + } + } + private void addObbStateLocked(ObbState obbState) throws RemoteException { final IBinder binder = obbState.getBinder(); List<ObbState> obbStates = mObbMounts.get(binder); diff --git a/services/tests/servicestests/src/com/android/server/MockStorageManager.java b/services/tests/servicestests/src/com/android/server/MockStorageManager.java index 031a3b39055a..17c8ec2fac13 100644 --- a/services/tests/servicestests/src/com/android/server/MockStorageManager.java +++ b/services/tests/servicestests/src/com/android/server/MockStorageManager.java @@ -490,4 +490,14 @@ public class MockStorageManager implements IStorageManager { throw new UnsupportedOperationException(); } + @Override + public long getAllocatableBytes(String path, int flags) { + throw new UnsupportedOperationException(); + } + + @Override + public void allocateBytes(String path, long bytes, int flags) { + throw new UnsupportedOperationException(); + } + } diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp index f7e0f8f55fd4..0501a3bb5fca 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -1634,7 +1634,7 @@ class LinkCommand { if (options_.static_lib) { if (options_.table_splitter_options.config_filter != nullptr || - options_.table_splitter_options.preferred_density) { + !options_.table_splitter_options.preferred_densities.empty()) { context_->GetDiagnostics() ->Warn(DiagMessage() << "can't strip resources when building static library"); @@ -2107,8 +2107,7 @@ int Link(const std::vector<StringPiece>& args) { << "Preferred density must only be a density value"); return 1; } - options.table_splitter_options.preferred_density = - preferred_density_config.density; + options.table_splitter_options.preferred_densities.push_back(preferred_density_config.density); } if (!options.static_lib && stable_id_file_path) { diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp index 38cfd2e61054..27e13d81ff49 100644 --- a/tools/aapt2/split/TableSplitter.cpp +++ b/tools/aapt2/split/TableSplitter.cpp @@ -19,6 +19,7 @@ #include <algorithm> #include <map> #include <set> +#include <unordered_set> #include <unordered_map> #include <vector> @@ -124,29 +125,35 @@ class SplitValueSelector { * leaving only the preferred density behind. */ static void MarkNonPreferredDensitiesAsClaimed( - uint16_t preferred_density, const ConfigDensityGroups& density_groups, + const std::vector<uint16_t>& preferred_densities, const ConfigDensityGroups& density_groups, ConfigClaimedMap* config_claimed_map) { for (auto& entry : density_groups) { const ConfigDescription& config = entry.first; const std::vector<ResourceConfigValue*>& related_values = entry.second; - ConfigDescription target_density = config; - target_density.density = preferred_density; - ResourceConfigValue* best_value = nullptr; + // There can be multiple best values if there are multiple preferred densities. + std::unordered_set<ResourceConfigValue*> best_values; + + // For each preferred density, find the value that is the best. + for (uint16_t preferred_density : preferred_densities) { + ConfigDescription target_density = config; + target_density.density = preferred_density; + ResourceConfigValue* best_value = nullptr; + for (ResourceConfigValue* this_value : related_values) { + if (!best_value || this_value->config.isBetterThan(best_value->config, &target_density)) { + best_value = this_value; + } + } + CHECK(best_value != nullptr); + best_values.insert(best_value); + } + + // Claim all the values that aren't the best so that they will be removed from the base. for (ResourceConfigValue* this_value : related_values) { - if (!best_value) { - best_value = this_value; - } else if (this_value->config.isBetterThan(best_value->config, - &target_density)) { - // Claim the previous value so that it is not included in the base. - (*config_claimed_map)[best_value] = true; - best_value = this_value; - } else { - // Claim this value so that it is not included in the base. + if (best_values.find(this_value) == best_values.end()) { (*config_claimed_map)[this_value] = true; } } - CHECK(best_value != nullptr); } } bool TableSplitter::VerifySplitConstraints(IAaptContext* context) { @@ -263,8 +270,8 @@ void TableSplitter::SplitTable(ResourceTable* original_table) { } } - if (options_.preferred_density) { - MarkNonPreferredDensitiesAsClaimed(options_.preferred_density.value(), + if (!options_.preferred_densities.empty()) { + MarkNonPreferredDensitiesAsClaimed(options_.preferred_densities, density_groups, &config_claimed_map); } diff --git a/tools/aapt2/split/TableSplitter.h b/tools/aapt2/split/TableSplitter.h index 1ae327120796..6aec2576261e 100644 --- a/tools/aapt2/split/TableSplitter.h +++ b/tools/aapt2/split/TableSplitter.h @@ -34,14 +34,14 @@ struct SplitConstraints { struct TableSplitterOptions { /** - * The preferred density to keep in the table, stripping out all others. + * The preferred densities to keep in the table, stripping out all others. + * If empty, no stripping is done. */ - Maybe<uint16_t> preferred_density; + std::vector<uint16_t> preferred_densities; /** * Configuration filter that determines which resource configuration values - * end up in - * the final table. + * end up in the final table. */ IConfigFilter* config_filter = nullptr; }; diff --git a/tools/aapt2/split/TableSplitter_test.cpp b/tools/aapt2/split/TableSplitter_test.cpp index 088dac374458..d52f4b446b8f 100644 --- a/tools/aapt2/split/TableSplitter_test.cpp +++ b/tools/aapt2/split/TableSplitter_test.cpp @@ -39,7 +39,7 @@ TEST(TableSplitterTest, NoSplitPreferredDensity) { .Build(); TableSplitterOptions options; - options.preferred_density = ConfigDescription::DENSITY_XHIGH; + options.preferred_densities.push_back(ConfigDescription::DENSITY_XHIGH); TableSplitter splitter({}, options); splitter.SplitTable(table.get()); @@ -58,6 +58,51 @@ TEST(TableSplitterTest, NoSplitPreferredDensity) { EXPECT_NE(nullptr, test::GetValue<Id>(table.get(), "android:string/one")); } +TEST(TableSplitterTest, NoSplitMultiplePreferredDensities) { + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .AddFileReference("android:drawable/icon", + "res/drawable-mdpi/icon.png", + test::ParseConfigOrDie("mdpi")) + .AddFileReference("android:drawable/icon", + "res/drawable-hdpi/icon.png", + test::ParseConfigOrDie("hdpi")) + .AddFileReference("android:drawable/icon", + "res/drawable-xhdpi/icon.png", + test::ParseConfigOrDie("xhdpi")) + .AddFileReference("android:drawable/icon", + "res/drawable-xxhdpi/icon.png", + test::ParseConfigOrDie("xxhdpi")) + .AddSimple("android:string/one") + .Build(); + + TableSplitterOptions options; + options.preferred_densities.push_back(ConfigDescription::DENSITY_LOW); + options.preferred_densities.push_back(ConfigDescription::DENSITY_XXXHIGH); + TableSplitter splitter({}, options); + splitter.SplitTable(table.get()); + + // Densities remaining: + // "mdpi" is the closest available density for the requested "ldpi" density. + EXPECT_NE(nullptr, test::GetValueForConfig<FileReference>( + table.get(), "android:drawable/icon", + test::ParseConfigOrDie("mdpi"))); + // "xxhdpi" is the closest available density for the requested "xxxhdpi" density. + EXPECT_NE(nullptr, test::GetValueForConfig<FileReference>( + table.get(), "android:drawable/icon", + test::ParseConfigOrDie("xxhdpi"))); + EXPECT_NE(nullptr, test::GetValue<Id>(table.get(), "android:string/one")); + + // Removed densities: + EXPECT_EQ(nullptr, test::GetValueForConfig<FileReference>( + table.get(), "android:drawable/icon", + test::ParseConfigOrDie("hdpi"))); + EXPECT_EQ(nullptr, test::GetValueForConfig<FileReference>( + table.get(), "android:drawable/icon", + test::ParseConfigOrDie("xhdpi"))); +} + + TEST(TableSplitterTest, SplitTableByDensity) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() diff --git a/tools/aapt2/strip/Strip.cpp b/tools/aapt2/strip/Strip.cpp index c34cfbf3580e..7260649c26d4 100644 --- a/tools/aapt2/strip/Strip.cpp +++ b/tools/aapt2/strip/Strip.cpp @@ -79,18 +79,13 @@ class StripCommand { context_->GetDiagnostics()->Note(DiagMessage() << "Stripping APK..."); } - // TODO(lecesne): Add support for more than one density. - if (options_.target_configs.size() > 1) { - context_->GetDiagnostics()->Error(DiagMessage() - << "Multiple densities not supported at the moment"); - return 1; - } - // Stripping the APK using the TableSplitter with no splits and the target - // density as the preferred density. The resource table is modified in + // densities as the preferred densities. The resource table is modified in // place in the LoadedApk. TableSplitterOptions splitter_options; - splitter_options.preferred_density = options_.target_configs[0].density; + for (auto& config : options_.target_configs) { + splitter_options.preferred_densities.push_back(config.density); + } std::vector<SplitConstraints> splits; TableSplitter splitter(splits, splitter_options); splitter.SplitTable(apk->GetResourceTable()); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java index 468710bbb470..4338ca0eacdc 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java @@ -228,7 +228,8 @@ public class BridgeIInputMethodManager implements IInputMethodManager { @Override public InputBindResult startInputOrWindowGainedFocus( /* @InputMethodClient.StartInputReason */ int startInputReason, - IInputMethodClient client, IBinder windowToken, int controlFlags, int softInputMode, + IInputMethodClient client, IBinder windowToken, int controlFlags, + /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode, int windowFlags, EditorInfo attribute, IInputContext inputContext, /* @InputConnectionInspector.MissingMethodFlags */ int missingMethodFlags) throws RemoteException { |