diff options
30 files changed, 798 insertions, 286 deletions
diff --git a/api/current.txt b/api/current.txt index dd70a3298aeb..c81284b1af46 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5855,7 +5855,7 @@ package android.app.admin { method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName); method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName); method public int getCurrentFailedPasswordAttempts(); - method public java.lang.String getDeviceOwnerLockScreenInfo(); + method public java.lang.CharSequence getDeviceOwnerLockScreenInfo(); method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName); method public int getKeyguardDisabledFeatures(android.content.ComponentName); method public java.lang.String getLongSupportMessage(android.content.ComponentName); @@ -5926,7 +5926,7 @@ package android.app.admin { method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException; method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean); method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean); - method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String); + method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence); method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String); method public boolean setKeyguardDisabled(android.content.ComponentName, boolean); method public void setKeyguardDisabledFeatures(android.content.ComponentName, int); @@ -10001,6 +10001,7 @@ package android.content.pm { method public android.content.Intent getIntent(); method public long getLastChangedTimestamp(); method public java.lang.String getPackageName(); + method public java.lang.String getText(); method public java.lang.String getTitle(); method public int getWeight(); method public boolean hasIconFile(); @@ -10028,6 +10029,7 @@ package android.content.pm { method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon); method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String); method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent); + method public android.content.pm.ShortcutInfo.Builder setText(java.lang.String); method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String); method public android.content.pm.ShortcutInfo.Builder setWeight(int); } @@ -30370,7 +30372,7 @@ package android.printservice { method public final boolean isDestroyed(); method public final boolean isPrinterDiscoveryStarted(); method public abstract void onDestroy(); - method public void onRequestCustomPrinterIcon(android.print.PrinterId, android.printservice.CustomPrinterIconCallback); + method public void onRequestCustomPrinterIcon(android.print.PrinterId, android.os.CancellationSignal, android.printservice.CustomPrinterIconCallback); method public abstract void onStartPrinterDiscovery(java.util.List<android.print.PrinterId>); method public abstract void onStartPrinterStateTracking(android.print.PrinterId); method public abstract void onStopPrinterDiscovery(); diff --git a/api/system-current.txt b/api/system-current.txt index 0fe632d27d88..071d1f123ebe 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5995,7 +5995,7 @@ package android.app.admin { method public deprecated java.lang.String getDeviceInitializerApp(); method public deprecated android.content.ComponentName getDeviceInitializerComponent(); method public java.lang.String getDeviceOwner(); - method public java.lang.String getDeviceOwnerLockScreenInfo(); + method public java.lang.CharSequence getDeviceOwnerLockScreenInfo(); method public java.lang.String getDeviceOwnerNameOnAnyUser(); method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName); method public int getKeyguardDisabledFeatures(android.content.ComponentName); @@ -6074,7 +6074,7 @@ package android.app.admin { method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException; method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean); method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean); - method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String); + method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence); method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String); method public boolean setKeyguardDisabled(android.content.ComponentName, boolean); method public void setKeyguardDisabledFeatures(android.content.ComponentName, int); @@ -10399,6 +10399,7 @@ package android.content.pm { method public android.content.Intent getIntent(); method public long getLastChangedTimestamp(); method public java.lang.String getPackageName(); + method public java.lang.String getText(); method public java.lang.String getTitle(); method public int getWeight(); method public boolean hasIconFile(); @@ -10426,6 +10427,7 @@ package android.content.pm { method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon); method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String); method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent); + method public android.content.pm.ShortcutInfo.Builder setText(java.lang.String); method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String); method public android.content.pm.ShortcutInfo.Builder setWeight(int); } @@ -32685,7 +32687,7 @@ package android.printservice { method public final boolean isDestroyed(); method public final boolean isPrinterDiscoveryStarted(); method public abstract void onDestroy(); - method public void onRequestCustomPrinterIcon(android.print.PrinterId, android.printservice.CustomPrinterIconCallback); + method public void onRequestCustomPrinterIcon(android.print.PrinterId, android.os.CancellationSignal, android.printservice.CustomPrinterIconCallback); method public abstract void onStartPrinterDiscovery(java.util.List<android.print.PrinterId>); method public abstract void onStartPrinterStateTracking(android.print.PrinterId); method public abstract void onStopPrinterDiscovery(); diff --git a/api/test-current.txt b/api/test-current.txt index d59fa272088d..821614631d65 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -5859,7 +5859,7 @@ package android.app.admin { method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName); method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName); method public int getCurrentFailedPasswordAttempts(); - method public java.lang.String getDeviceOwnerLockScreenInfo(); + method public java.lang.CharSequence getDeviceOwnerLockScreenInfo(); method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName); method public int getKeyguardDisabledFeatures(android.content.ComponentName); method public java.lang.String getLongSupportMessage(android.content.ComponentName); @@ -5930,7 +5930,7 @@ package android.app.admin { method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException; method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean); method public void setCrossProfileContactsSearchDisabled(android.content.ComponentName, boolean); - method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String); + method public void setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.CharSequence); method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String); method public boolean setKeyguardDisabled(android.content.ComponentName, boolean); method public void setKeyguardDisabledFeatures(android.content.ComponentName, int); @@ -10011,6 +10011,7 @@ package android.content.pm { method public android.content.Intent getIntent(); method public long getLastChangedTimestamp(); method public java.lang.String getPackageName(); + method public java.lang.String getText(); method public java.lang.String getTitle(); method public int getWeight(); method public boolean hasIconFile(); @@ -10038,6 +10039,7 @@ package android.content.pm { method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon); method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String); method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent); + method public android.content.pm.ShortcutInfo.Builder setText(java.lang.String); method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String); method public android.content.pm.ShortcutInfo.Builder setWeight(int); } @@ -30439,7 +30441,7 @@ package android.printservice { method public final boolean isDestroyed(); method public final boolean isPrinterDiscoveryStarted(); method public abstract void onDestroy(); - method public void onRequestCustomPrinterIcon(android.print.PrinterId, android.printservice.CustomPrinterIconCallback); + method public void onRequestCustomPrinterIcon(android.print.PrinterId, android.os.CancellationSignal, android.printservice.CustomPrinterIconCallback); method public abstract void onStartPrinterDiscovery(java.util.List<android.print.PrinterId>); method public abstract void onStartPrinterStateTracking(android.print.PrinterId); method public abstract void onStopPrinterDiscovery(); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index e7427bfabe01..502d1cfd60a4 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3736,24 +3736,22 @@ public class DevicePolicyManager { * * @param admin The name of the admin component to check. * @param info Device owner information which will be displayed instead of the user owner info. - * @return Whether the device owner information has been set. * @throws SecurityException if {@code admin} is not a device owner. */ - public boolean setDeviceOwnerLockScreenInfo(@NonNull ComponentName admin, String info) { + public void setDeviceOwnerLockScreenInfo(@NonNull ComponentName admin, CharSequence info) { if (mService != null) { try { - return mService.setDeviceOwnerLockScreenInfo(admin, info); + mService.setDeviceOwnerLockScreenInfo(admin, info); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } - return false; } /** * @return The device owner information. If it is not set returns {@code null}. */ - public String getDeviceOwnerLockScreenInfo() { + public CharSequence getDeviceOwnerLockScreenInfo() { if (mService != null) { try { return mService.getDeviceOwnerLockScreenInfo(); diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index aed220dd5705..97383a3f4963 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -135,8 +135,8 @@ interface IDevicePolicyManager { void clearProfileOwner(in ComponentName who); boolean hasUserSetupCompleted(); - boolean setDeviceOwnerLockScreenInfo(in ComponentName who, String deviceOwnerInfo); - String getDeviceOwnerLockScreenInfo(); + void setDeviceOwnerLockScreenInfo(in ComponentName who, CharSequence deviceOwnerInfo); + CharSequence getDeviceOwnerLockScreenInfo(); String[] setPackagesSuspended(in ComponentName admin, in String[] packageNames, boolean suspended); boolean getPackageSuspended(in ComponentName admin, String packageName); diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java index f6d2268343a3..b8a40dc6895a 100644 --- a/core/java/android/bluetooth/BluetoothGatt.java +++ b/core/java/android/bluetooth/BluetoothGatt.java @@ -422,7 +422,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mAuthRetry = true; mService.writeDescriptor(mClientIf, address, handle, - descriptor.getCharacteristic().getWriteType(), + BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT, AUTHENTICATION_MITM, descriptor.getValue()); return; } catch (RemoteException e) { @@ -943,7 +943,8 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(), - characteristic.getWriteType(), AUTHENTICATION_NONE, descriptor.getValue()); + BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT, AUTHENTICATION_NONE, + descriptor.getValue()); } catch (RemoteException e) { Log.e(TAG,"",e); mDeviceBusy = false; diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index a0238fb07e5e..6fce36be43ab 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -437,10 +437,9 @@ interface IPackageManager { void performFstrimIfNeeded(); /** - * Ask the package manager to extract packages if needed, to save - * the VM unzipping the APK in memory during launch. + * Ask the package manager to update packages if needed. */ - void extractPackagesIfNeeded(); + void updatePackagesIfNeeded(); /** * Notify the package manager that a package is going to be used. diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index ae75e3f9a156..7408c34538ab 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -118,6 +118,9 @@ public class ShortcutInfo implements Parcelable { @NonNull private String mTitle; + @Nullable + private String mText; + /** * Intent *with extras removed*. */ @@ -157,6 +160,7 @@ public class ShortcutInfo implements Parcelable { mActivityComponent = b.mActivityComponent; mIcon = b.mIcon; mTitle = b.mTitle; + mText = b.mText; mIntent = b.mIntent; if (mIntent != null) { final Bundle intentExtras = mIntent.getExtras(); @@ -176,6 +180,7 @@ public class ShortcutInfo implements Parcelable { * @hide */ public void enforceMandatoryFields() { + Preconditions.checkStringNotEmpty(mId, "Shortcut ID must be provided"); Preconditions.checkStringNotEmpty(mTitle, "Shortcut title must be provided"); Preconditions.checkNotNull(mIntent, "Shortcut Intent must be provided"); } @@ -195,16 +200,17 @@ public class ShortcutInfo implements Parcelable { if ((cloneFlags & CLONE_REMOVE_ICON) == 0) { mIcon = source.mIcon; mBitmapPath = source.mBitmapPath; + mIconResourceId = source.mIconResourceId; } mTitle = source.mTitle; + mText = source.mText; if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) { mIntent = source.mIntent; mIntentPersistableExtras = source.mIntentPersistableExtras; } mWeight = source.mWeight; mExtras = source.mExtras; - mIconResourceId = source.mIconResourceId; } else { // Set this bit. mFlags |= FLAG_KEY_FIELDS_ONLY; @@ -244,6 +250,9 @@ public class ShortcutInfo implements Parcelable { if (source.mTitle != null) { mTitle = source.mTitle; } + if (source.mText != null) { + mText = source.mText; + } if (source.mIntent != null) { mIntent = source.mIntent; mIntentPersistableExtras = source.mIntentPersistableExtras; @@ -305,6 +314,8 @@ public class ShortcutInfo implements Parcelable { private String mTitle; + private String mText; + private Intent mIntent; private int mWeight; @@ -368,6 +379,15 @@ public class ShortcutInfo implements Parcelable { } /** + * Sets the text of a shortcut. This is an optional field. + */ + @NonNull + public Builder setText(@NonNull String text) { + mText = Preconditions.checkStringNotEmpty(text, "text"); + return this; + } + + /** * Sets the intent of a shortcut. This is a mandatory field. The extras must only contain * persistable information. (See {@link PersistableBundle}). */ @@ -457,6 +477,14 @@ public class ShortcutInfo implements Parcelable { } /** + * Return the shortcut text. + */ + @Nullable + public String getText() { + return mText; + } + + /** * Return the intent. * * <p>All shortcuts must have an intent, but this method will return null when @@ -630,6 +658,7 @@ public class ShortcutInfo implements Parcelable { mActivityComponent = source.readParcelable(cl); mIcon = source.readParcelable(cl); mTitle = source.readString(); + mText = source.readString(); mIntent = source.readParcelable(cl); mIntentPersistableExtras = source.readParcelable(cl); mWeight = source.readInt(); @@ -647,6 +676,7 @@ public class ShortcutInfo implements Parcelable { dest.writeParcelable(mActivityComponent, flags); dest.writeParcelable(mIcon, flags); dest.writeString(mTitle); + dest.writeString(mText); dest.writeParcelable(mIntent, flags); dest.writeParcelable(mIntentPersistableExtras, flags); dest.writeInt(mWeight); @@ -708,6 +738,9 @@ public class ShortcutInfo implements Parcelable { sb.append(", title="); sb.append(secure ? "***" : mTitle); + sb.append(", text="); + sb.append(secure ? "***" : mText); + sb.append(", icon="); sb.append(mIcon); @@ -744,7 +777,8 @@ public class ShortcutInfo implements Parcelable { /** @hide */ public ShortcutInfo(String id, String packageName, ComponentName activityComponent, - Icon icon, String title, Intent intent, PersistableBundle intentPersistableExtras, + Icon icon, String title, String text, Intent intent, + PersistableBundle intentPersistableExtras, int weight, PersistableBundle extras, long lastChangedTimestamp, int flags, int iconResId, String bitmapPath) { mId = id; @@ -752,6 +786,7 @@ public class ShortcutInfo implements Parcelable { mActivityComponent = activityComponent; mIcon = icon; mTitle = title; + mText = text; mIntent = intent; mIntentPersistableExtras = intentPersistableExtras; mWeight = weight; diff --git a/core/java/android/printservice/PrinterDiscoverySession.java b/core/java/android/printservice/PrinterDiscoverySession.java index cd5a903c4e69..7b9533d73216 100644 --- a/core/java/android/printservice/PrinterDiscoverySession.java +++ b/core/java/android/printservice/PrinterDiscoverySession.java @@ -18,6 +18,7 @@ package android.printservice; import android.annotation.NonNull; import android.content.pm.ParceledListSlice; +import android.os.CancellationSignal; import android.os.RemoteException; import android.print.PrinterCapabilitiesInfo; import android.print.PrinterId; @@ -412,11 +413,13 @@ public abstract class PrinterDiscoverySession { * service. * * @param printerId The printer to icon belongs to. + * @param cancellationSignal Signal used to cancel the request * @param callback Callback for returning the icon to the print spooler. * * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon() */ public void onRequestCustomPrinterIcon(@NonNull PrinterId printerId, + @NonNull CancellationSignal cancellationSignal, @NonNull CustomPrinterIconCallback callback) { } @@ -533,7 +536,7 @@ public abstract class PrinterDiscoverySession { if (!mIsDestroyed && mObserver != null) { CustomPrinterIconCallback callback = new CustomPrinterIconCallback(printerId, mObserver); - onRequestCustomPrinterIcon(printerId, callback); + onRequestCustomPrinterIcon(printerId, new CancellationSignal(), callback); } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 816d9c48539e..f6958738353b 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -6749,7 +6749,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @Override public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { - // Do nothing + // Re-dispatch up the tree by default + dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, null); } /** @@ -6757,7 +6758,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ @Override public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { - // Do nothing + // Re-dispatch up the tree by default + dispatchNestedPreScroll(dx, dy, consumed, null); } /** @@ -6765,7 +6767,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ @Override public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { - return false; + // Re-dispatch up the tree by default + return dispatchNestedFling(velocityX, velocityY, consumed); } /** @@ -6773,7 +6776,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ @Override public boolean onNestedPreFling(View target, float velocityX, float velocityY) { - return false; + // Re-dispatch up the tree by default + return dispatchNestedPreFling(velocityX, velocityY); } /** diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java index 43cf5a1cdc0b..ee716df58548 100644 --- a/core/java/android/widget/SimpleMonthView.java +++ b/core/java/android/widget/SimpleMonthView.java @@ -16,6 +16,9 @@ package android.widget; +import com.android.internal.R; +import com.android.internal.widget.ExploreByTouchHelper; + import android.annotation.Nullable; import android.content.Context; import android.content.res.ColorStateList; @@ -44,14 +47,13 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; -import com.android.internal.R; -import com.android.internal.widget.ExploreByTouchHelper; - import java.text.NumberFormat; import java.util.Arrays; import java.util.Calendar; import java.util.Locale; +import libcore.icu.LocaleData; + /** * A calendar-like view displaying a specified month and the appropriate selectable day numbers * within the specified month. @@ -66,7 +68,6 @@ class SimpleMonthView extends View { private static final int DEFAULT_WEEK_START = Calendar.SUNDAY; private static final String MONTH_YEAR_FORMAT = "MMMMy"; - private static final String DAY_OF_WEEK_FORMAT = "EEEEE"; private static final int SELECTED_HIGHLIGHT_ALPHA = 0xB0; @@ -80,6 +81,7 @@ class SimpleMonthView extends View { private final Paint mDayHighlightPaint = new Paint(); private final Paint mDayHighlightSelectorPaint = new Paint(); + /** Array of single-character weekday labels ordered by column index. */ private final String[] mDayOfWeekLabels = new String[7]; private final Calendar mCalendar; @@ -120,7 +122,7 @@ class SimpleMonthView extends View { */ private int mToday = DEFAULT_SELECTED_DAY; - /** The first day of the week (ex. Calendar.SUNDAY). */ + /** The first day of the week (ex. Calendar.SUNDAY) indexed from one. */ private int mWeekStart = DEFAULT_WEEK_START; /** The number of days (ex. 28) in the current month. */ @@ -199,13 +201,11 @@ class SimpleMonthView extends View { Log.d(LOG_TAG, "mWeekStart => " + mWeekStart); } - final Calendar calendar = Calendar.getInstance(mLocale); - calendar.setFirstDayOfWeek(mWeekStart); - - final SimpleDateFormat formatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, mLocale); - for (int i = 0; i < 7; i++) { - calendar.set(Calendar.DAY_OF_WEEK, i); - mDayOfWeekLabels[i] = formatter.format(calendar.getTime()); + // Use tiny (e.g. single-character) weekday names from ICU. The indices + // for this list correspond to Calendar days, e.g. SUNDAY is index 1. + final String[] tinyWeekdayNames = LocaleData.get(mLocale).tinyWeekdayNames; + for (int i = 0; i < DAYS_IN_WEEK; i++) { + mDayOfWeekLabels[i] = tinyWeekdayNames[(mWeekStart + i - 1) % DAYS_IN_WEEK + 1]; } if (DEBUG_WRONG_DATE) { @@ -662,8 +662,7 @@ class SimpleMonthView extends View { colCenterRtl = colCenter; } - final int dayOfWeek = (col + mWeekStart) % DAYS_IN_WEEK; - final String label = mDayOfWeekLabels[dayOfWeek]; + final String label = mDayOfWeekLabels[col]; canvas.drawText(label, colCenterRtl, rowCenter - halfLineHeight, p); } } @@ -813,6 +812,11 @@ class SimpleMonthView extends View { */ void setMonthParams(int selectedDay, int month, int year, int weekStart, int enabledDayStart, int enabledDayEnd) { + if (DEBUG_WRONG_DATE) { + Log.d(LOG_TAG, "setMonthParams(" + selectedDay + ", " + month + ", " + year + ", " + + weekStart + ", " + enabledDayStart + ", " + enabledDayEnd + ")"); + } + mActivatedDay = selectedDay; if (isValidMonth(month)) { @@ -849,6 +853,14 @@ class SimpleMonthView extends View { mTouchHelper.invalidateRoot(); updateMonthYearLabel(); + + if (DEBUG_WRONG_DATE) { + Log.d(LOG_TAG, "mMonth = " + mMonth); + Log.d(LOG_TAG, "mDayOfWeekStart = " + mDayOfWeekStart); + Log.d(LOG_TAG, "mWeekStart = " + mWeekStart); + Log.d(LOG_TAG, "mDaysInMonth = " + mDaysInMonth); + Log.d(LOG_TAG, "mToday = " + mToday); + } } private static int getDaysInMonth(int month, int year) { diff --git a/core/tests/coretests/src/android/print/BasePrintTest.java b/core/tests/coretests/src/android/print/BasePrintTest.java index c9bc8aaae0cb..d56a405ba0bb 100644 --- a/core/tests/coretests/src/android/print/BasePrintTest.java +++ b/core/tests/coretests/src/android/print/BasePrintTest.java @@ -28,6 +28,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; +import android.os.CancellationSignal; import android.os.ParcelFileDescriptor; import android.os.SystemClock; import android.print.PrintAttributes; @@ -281,7 +282,8 @@ public abstract class BasePrintTest extends InstrumentationTestCase { } if (onRequestCustomPrinterIcon != null) { doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon( - any(PrinterId.class), any(CustomPrinterIconCallback.class)); + any(PrinterId.class), any(CancellationSignal.class), + any(CustomPrinterIconCallback.class)); } if (onStopPrinterStateTracking != null) { doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking( diff --git a/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java b/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java index 26b7caeba15f..be002e29ff68 100644 --- a/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java +++ b/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java @@ -16,6 +16,7 @@ package android.print.mockservice; +import android.os.CancellationSignal; import android.print.PrinterId; import android.printservice.CustomPrinterIconCallback; @@ -42,7 +43,7 @@ public abstract class PrinterDiscoverySessionCallbacks { public abstract void onStartPrinterStateTracking(PrinterId printerId); public abstract void onRequestCustomPrinterIcon(PrinterId printerId, - CustomPrinterIconCallback callback); + CancellationSignal cancellationSignal, CustomPrinterIconCallback callback); public abstract void onStopPrinterStateTracking(PrinterId printerId); diff --git a/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java b/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java index 04683f267aab..e132d79cfd8b 100644 --- a/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java +++ b/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java @@ -16,6 +16,7 @@ package android.print.mockservice; +import android.os.CancellationSignal; import android.print.PrinterId; import android.printservice.CustomPrinterIconCallback; import android.printservice.PrintService; @@ -70,9 +71,9 @@ public class StubbablePrinterDiscoverySession extends PrinterDiscoverySession { @Override public void onRequestCustomPrinterIcon(PrinterId printerId, - CustomPrinterIconCallback callback) { + CancellationSignal cancellationSignal, CustomPrinterIconCallback callback) { if (mCallbacks != null) { - mCallbacks.onRequestCustomPrinterIcon(printerId, callback); + mCallbacks.onRequestCustomPrinterIcon(printerId, cancellationSignal, callback); } } diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index 4bf08527b594..cd2d51d4dd79 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -693,7 +693,7 @@ public class ExifInterface { */ public ExifInterface(FileDescriptor fileDescriptor) throws IOException { if (fileDescriptor == null) { - throw new IllegalArgumentException("parcelFileDescriptor cannot be null"); + throw new IllegalArgumentException("fileDescriptor cannot be null"); } mAssetInputStream = null; mFilename = null; @@ -705,7 +705,7 @@ public class ExifInterface { try { fileDescriptor = Os.dup(fileDescriptor); } catch (ErrnoException e) { - e.rethrowAsIOException(); + throw e.rethrowAsIOException(); } } else { mSeekableFileDescriptor = null; @@ -811,6 +811,9 @@ public class ExifInterface { */ public void setAttribute(String tag, String value) { for (int i = 0 ; i < EXIF_TAGS.length; ++i) { + if (i == IFD_THUMBNAIL_HINT && !mHasThumbnail) { + continue; + } if (sExifTagMapsForWriting[i].containsKey(tag)) { mAttributes[i].put(tag, value); } @@ -818,6 +821,35 @@ public class ExifInterface { } /** + * Update the values of the tags in the tag groups if any value for the tag already was stored. + * + * @param tag the name of the tag. + * @param value the value of the tag. + * @return Returns {@code true} if updating is placed. + */ + private boolean updateAttribute(String tag, String value) { + boolean updated = false; + for (int i = 0 ; i < EXIF_TAGS.length; ++i) { + if (mAttributes[i].containsKey(tag)) { + mAttributes[i].put(tag, value); + updated = true; + } + } + return updated; + } + + /** + * Remove any values of the specified tag. + * + * @param tag the name of the tag. + */ + private void removeAttribute(String tag) { + for (int i = 0 ; i < EXIF_TAGS.length; ++i) { + mAttributes[i].remove(tag); + } + } + + /** * This function decides which parser to read the image data according to the given input stream * type and the content of the input stream. In each case, it reads the first three bytes to * determine whether the image data format is JPEG or not. @@ -853,7 +885,7 @@ public class ExifInterface { } catch (IOException e) { // Ignore exceptions in order to keep the compatibility with the old versions of // ExifInterface. - Log.w(TAG, "Invalid JPEG: ExifInterface got an unsupported image format file" + Log.w(TAG, "Invalid image: ExifInterface got an unsupported image format file" + "(ExifInterface supports JPEG and some RAW image formats only) " + "or a corrupted JPEG file to ExifInterface.", e); } finally { @@ -882,27 +914,22 @@ public class ExifInterface { // Mark for disabling the save feature. mIsRaw = true; - for (Map.Entry entry : (Set<Map.Entry>) map.entrySet()) { - String attrName = (String) entry.getKey(); + String value = (String) map.remove(TAG_HAS_THUMBNAIL); + mHasThumbnail = value != null && value.equalsIgnoreCase("true"); + value = (String) map.remove(TAG_THUMBNAIL_OFFSET); + if (value != null) { + mThumbnailOffset = Integer.parseInt(value); + } + value = (String) map.remove(TAG_THUMBNAIL_LENGTH); + if (value != null) { + mThumbnailLength = Integer.parseInt(value); + } + mThumbnailBytes = (byte[]) map.remove(TAG_THUMBNAIL_DATA); - switch (attrName) { - case TAG_HAS_THUMBNAIL: - mHasThumbnail = ((String) entry.getValue()).equalsIgnoreCase("true"); - break; - case TAG_THUMBNAIL_OFFSET: - mThumbnailOffset = Integer.parseInt((String) entry.getValue()); - break; - case TAG_THUMBNAIL_LENGTH: - mThumbnailLength = Integer.parseInt((String) entry.getValue()); - break; - case TAG_THUMBNAIL_DATA: - mThumbnailBytes = (byte[]) entry.getValue(); - break; - default: - setAttribute(attrName, (String) entry.getValue()); - break; - } + for (Map.Entry entry : (Set<Map.Entry>) map.entrySet()) { + setAttribute((String) entry.getKey(), (String) entry.getValue()); } + return true; } @@ -928,7 +955,7 @@ public class ExifInterface { /** * Save the tag data into the original image file. This is expensive because it involves * copying all the data from one file to another and deleting the old file and renaming the - * other. It's best to use{@link #setAttribute(String,String)} to set all attributes to write + * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write * and make a single call rather than multiple calls for each attribute. */ public void saveAttributes() throws IOException { @@ -963,7 +990,7 @@ public class ExifInterface { Streams.copy(in, out); } } catch (ErrnoException e) { - e.rethrowAsIOException(); + throw e.rethrowAsIOException(); } finally { IoUtils.closeQuietly(in); IoUtils.closeQuietly(out); @@ -982,7 +1009,7 @@ public class ExifInterface { } saveJpegAttributes(in, out); } catch (ErrnoException e) { - e.rethrowAsIOException(); + throw e.rethrowAsIOException(); } finally { IoUtils.closeQuietly(in); IoUtils.closeQuietly(out); @@ -1276,7 +1303,8 @@ public class ExifInterface { throw new IOException("Invalid exif"); } length = 0; - setAttribute("UserComment", new String(bytes, Charset.forName("US-ASCII"))); + mAttributes[IFD_EXIF_HINT].put(TAG_USER_COMMENT, + new String(bytes, Charset.forName("US-ASCII"))); break; } @@ -1296,9 +1324,10 @@ public class ExifInterface { if (dataInputStream.skipBytes(1) != 1) { throw new IOException("Invalid SOFx"); } - setAttribute("ImageLength", + mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, + String.valueOf(dataInputStream.readUnsignedShort())); + mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, String.valueOf(dataInputStream.readUnsignedShort())); - setAttribute("ImageWidth", String.valueOf(dataInputStream.readUnsignedShort())); length -= 5; break; } @@ -1521,31 +1550,31 @@ public class ExifInterface { convertToInt(TAG_GPS_ALTITUDE_REF); convertToRational(TAG_GPS_LONGITUDE); convertToRational(TAG_GPS_LATITUDE); - convertToTimetamp(TAG_GPS_TIMESTAMP); + convertToTimestamp(TAG_GPS_TIMESTAMP); // The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag. - String valueOfDateTimeOriginal = getAttribute("DateTimeOriginal"); + String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL); if (valueOfDateTimeOriginal != null) { - setAttribute(TAG_DATETIME, valueOfDateTimeOriginal); + mAttributes[IFD_TIFF_HINT].put(TAG_DATETIME, valueOfDateTimeOriginal); } // Add the default value. if (getAttribute(TAG_IMAGE_WIDTH) == null) { - setAttribute(TAG_IMAGE_WIDTH, "0"); + mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, "0"); } if (getAttribute(TAG_IMAGE_LENGTH) == null) { - setAttribute(TAG_IMAGE_LENGTH, "0"); + mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, "0"); } if (getAttribute(TAG_ORIENTATION) == null) { - setAttribute(TAG_ORIENTATION, "0"); + mAttributes[IFD_TIFF_HINT].put(TAG_ORIENTATION, "0"); } if (getAttribute(TAG_LIGHT_SOURCE) == null) { - setAttribute(TAG_LIGHT_SOURCE, "0"); + mAttributes[IFD_EXIF_HINT].put(TAG_LIGHT_SOURCE, "0"); } } // Converts the tag value to timestamp; Otherwise deletes the given tag. - private void convertToTimetamp(String tagName) { + private void convertToTimestamp(String tagName) { String entryValue = getAttribute(tagName); if (entryValue == null) return; int dataFormat = getDataFormatOfExifEntryValue(entryValue); @@ -1566,9 +1595,9 @@ public class ExifInterface { int value = numerator / denominator; stringBuilder.append(String.format("%02d", value)); } - setAttribute(tagName, stringBuilder.toString()); + updateAttribute(tagName, stringBuilder.toString()); } else if (dataFormat != IFD_FORMAT_STRING) { - setAttribute(tagName, null); + removeAttribute(tagName); } } @@ -1595,14 +1624,14 @@ public class ExifInterface { } stringBuilder.append((double) numerator / denominator); } - setAttribute(tagName, stringBuilder.toString()); + updateAttribute(tagName, stringBuilder.toString()); break; } case IFD_FORMAT_DOUBLE: // Keep it as is. break; default: - setAttribute(tagName, null); + removeAttribute(tagName); break; } } @@ -1624,14 +1653,14 @@ public class ExifInterface { double doubleValue = Double.parseDouble(component); stringBuilder.append((int) (doubleValue * 10000.0)).append("/").append(10000); } - setAttribute(tagName, stringBuilder.toString()); + updateAttribute(tagName, stringBuilder.toString()); break; } case IFD_FORMAT_SRATIONAL: // Keep it as is. break; default: - setAttribute(tagName, null); + removeAttribute(tagName); break; } } @@ -1642,7 +1671,7 @@ public class ExifInterface { if (entryValue == null) return; int dataFormat = getDataFormatOfExifEntryValue(entryValue); if (dataFormat != IFD_FORMAT_SLONG) { - setAttribute(tagName, null); + removeAttribute(tagName); } } @@ -1758,7 +1787,7 @@ public class ExifInterface { String entryValue = readExifEntryValue( dataInputStream, dataFormat, numberOfComponents); if (entryValue != null) { - setAttribute(tagName, entryValue); + mAttributes[hint].put(tagName, entryValue); } } else { StringBuilder entryValueBuilder = new StringBuilder(); @@ -1769,7 +1798,7 @@ public class ExifInterface { entryValueBuilder.append(readExifEntryValue( dataInputStream, dataFormat, numberOfComponents)); } - setAttribute(tagName, entryValueBuilder.toString()); + mAttributes[hint].put(tagName, entryValueBuilder.toString()); } if (dataInputStream.peek() != nextEntryOffset) { @@ -1886,11 +1915,11 @@ public class ExifInterface { // Remove IFD pointer tags (we'll re-add it later.) for (ExifTag tag : IFD_POINTER_TAGS) { - setAttribute(tag.name, null); + removeAttribute(tag.name); } // Remove old thumbnail data - setAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name, null); - setAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name, null); + removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name); + removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name); // Remove null value tags. for (int hint = 0; hint < EXIF_TAGS.length; ++hint) { diff --git a/media/tests/MediaFrameworkTest/res/values/exifinterface.xml b/media/tests/MediaFrameworkTest/res/values/exifinterface.xml index d556ad33c04a..3c5dd3786e7c 100644 --- a/media/tests/MediaFrameworkTest/res/values/exifinterface.xml +++ b/media/tests/MediaFrameworkTest/res/values/exifinterface.xml @@ -103,7 +103,7 @@ <item>600</item> <item>800</item> <item>1</item> - <item /> + <item>0</item> </array> <array name="volantis_jpg"> <item>false</item> diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java index 6207f7d59809..dde9bdadebcd 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ExifInterfaceTest.java @@ -45,7 +45,7 @@ public class ExifInterfaceTest extends AndroidTestCase { private static final String TAG = ExifInterface.class.getSimpleName(); private static final boolean VERBOSE = false; // lots of logging - private static final double DIFFERENCE_TOLERANCE = .005; + private static final double DIFFERENCE_TOLERANCE = .001; // List of files. private static final String EXIF_BYTE_ORDER_II_JPEG = "image_exif_byte_order_ii.jpg"; @@ -111,11 +111,11 @@ public class ExifInterfaceTest extends AndroidTestCase { public final String gpsLongitudeRef; public final String gpsProcessingMethod; public final String gpsTimestamp; - public final String imageLength; - public final String imageWidth; + public final int imageLength; + public final int imageWidth; public final String iso; - public final String whiteBalance; - public final String orientation; + public final int orientation; + public final int whiteBalance; private static String getString(TypedArray typedArray, int index) { String stringValue = typedArray.getString(index); @@ -137,7 +137,7 @@ public class ExifInterfaceTest extends AndroidTestCase { longitude = typedArray.getFloat(5, 0f); altitude = typedArray.getFloat(6, 0f); - // Read values. + // Reads values. make = getString(typedArray, 7); model = getString(typedArray, 8); aperture = typedArray.getFloat(9, 0f); @@ -154,11 +154,11 @@ public class ExifInterfaceTest extends AndroidTestCase { gpsLongitudeRef = getString(typedArray, 20); gpsProcessingMethod = getString(typedArray, 21); gpsTimestamp = getString(typedArray, 22); - imageLength = getString(typedArray, 23); - imageWidth = getString(typedArray, 24); + imageLength = typedArray.getInt(23, 0); + imageWidth = typedArray.getInt(24, 0); iso = getString(typedArray, 25); - orientation = getString(typedArray, 26); - whiteBalance = getString(typedArray, 27); + orientation = typedArray.getInt(26, 0); + whiteBalance = typedArray.getInt(27, 0); typedArray.recycle(); } @@ -208,11 +208,13 @@ public class ExifInterfaceTest extends AndroidTestCase { + bitmap.getHeight()); } } else { - Log.e(TAG, fileName + " Corrupted image (no thumbnail)"); + Log.e(TAG, fileName + " Unexpected result: No thumbnails were found. " + + "A thumbnail is expected."); } } else { if (exifInterface.getThumbnail() != null) { - Log.e(TAG, fileName + " Corrupted image (a thumbnail exists)"); + Log.e(TAG, fileName + " Unexpected result: A thumbnail was found. " + + "No thumbnail is expected."); } else { Log.v(TAG, fileName + " No thumbnail"); } @@ -226,28 +228,27 @@ public class ExifInterfaceTest extends AndroidTestCase { Log.v(TAG, fileName + " Latitude = " + latLong[0]); Log.v(TAG, fileName + " Longitude = " + latLong[1]); } else { - Log.v(TAG, fileName + "No latlong data"); + Log.v(TAG, fileName + " No latlong data"); } // Prints values. for (String tagKey : EXIF_TAGS) { String tagValue = exifInterface.getAttribute(tagKey); - Log.v(TAG, fileName + "Key{" + tagKey + "} = '" + tagValue + "'"); + Log.v(TAG, fileName + " Key{" + tagKey + "} = '" + tagValue + "'"); } } - private void compareFloatTag(ExifInterface exifInterface, String tag, float expectedValue) { - String stringValue = exifInterface.getAttribute(tag); - float floatValue = 0f; - - if (stringValue != null) { - floatValue = Float.parseFloat(stringValue); - } + private void assertIntTag(ExifInterface exifInterface, String tag, int expectedValue) { + int intValue = exifInterface.getAttributeInt(tag, 0); + assertEquals(expectedValue, intValue); + } - assertEquals(expectedValue, floatValue, DIFFERENCE_TOLERANCE); + private void assertFloatTag(ExifInterface exifInterface, String tag, float expectedValue) { + double doubleValue = exifInterface.getAttributeDouble(tag, 0.0); + assertEquals(expectedValue, doubleValue, DIFFERENCE_TOLERANCE); } - private void compareStringTag(ExifInterface exifInterface, String tag, String expectedValue) { + private void assertStringTag(ExifInterface exifInterface, String tag, String expectedValue) { String stringValue = exifInterface.getAttribute(tag); if (stringValue != null) { stringValue = stringValue.trim(); @@ -257,7 +258,10 @@ public class ExifInterfaceTest extends AndroidTestCase { } private void compareWithExpectedValue(ExifInterface exifInterface, - ExpectedValue expectedValue) { + ExpectedValue expectedValue, String verboseTag) { + if (VERBOSE) { + printExifTagsAndValues(verboseTag, exifInterface); + } // Checks a thumbnail image. assertEquals(expectedValue.hasThumbnail, exifInterface.hasThumbnail()); if (expectedValue.hasThumbnail) { @@ -282,114 +286,159 @@ public class ExifInterfaceTest extends AndroidTestCase { assertEquals(expectedValue.altitude, exifInterface.getAltitude(.0), DIFFERENCE_TOLERANCE); // Checks values. - compareStringTag(exifInterface, ExifInterface.TAG_MAKE, expectedValue.make); - compareStringTag(exifInterface, ExifInterface.TAG_MODEL, expectedValue.model); - compareFloatTag(exifInterface, ExifInterface.TAG_APERTURE, expectedValue.aperture); - compareStringTag(exifInterface, ExifInterface.TAG_DATETIME, expectedValue.datetime); - compareFloatTag(exifInterface, ExifInterface.TAG_EXPOSURE_TIME, expectedValue.exposureTime); - compareFloatTag(exifInterface, ExifInterface.TAG_FLASH, expectedValue.flash); - compareStringTag(exifInterface, ExifInterface.TAG_FOCAL_LENGTH, expectedValue.focalLength); - compareStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE, expectedValue.gpsAltitude); - compareStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE_REF, + assertStringTag(exifInterface, ExifInterface.TAG_MAKE, expectedValue.make); + assertStringTag(exifInterface, ExifInterface.TAG_MODEL, expectedValue.model); + assertFloatTag(exifInterface, ExifInterface.TAG_APERTURE, expectedValue.aperture); + assertStringTag(exifInterface, ExifInterface.TAG_DATETIME, expectedValue.datetime); + assertFloatTag(exifInterface, ExifInterface.TAG_EXPOSURE_TIME, expectedValue.exposureTime); + assertFloatTag(exifInterface, ExifInterface.TAG_FLASH, expectedValue.flash); + assertStringTag(exifInterface, ExifInterface.TAG_FOCAL_LENGTH, expectedValue.focalLength); + assertStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE, expectedValue.gpsAltitude); + assertStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE_REF, expectedValue.gpsAltitudeRef); - compareStringTag(exifInterface, ExifInterface.TAG_GPS_DATESTAMP, - expectedValue.gpsDatestamp); - compareStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE, expectedValue.gpsLatitude); - compareStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE_REF, + assertStringTag(exifInterface, ExifInterface.TAG_GPS_DATESTAMP, expectedValue.gpsDatestamp); + assertStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE, expectedValue.gpsLatitude); + assertStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE_REF, expectedValue.gpsLatitudeRef); - compareStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE, - expectedValue.gpsLongitude); - compareStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE_REF, + assertStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE, expectedValue.gpsLongitude); + assertStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE_REF, expectedValue.gpsLongitudeRef); - compareStringTag(exifInterface, ExifInterface.TAG_GPS_PROCESSING_METHOD, + assertStringTag(exifInterface, ExifInterface.TAG_GPS_PROCESSING_METHOD, expectedValue.gpsProcessingMethod); - compareStringTag(exifInterface, ExifInterface.TAG_GPS_TIMESTAMP, - expectedValue.gpsTimestamp); - compareStringTag(exifInterface, ExifInterface.TAG_IMAGE_LENGTH, expectedValue.imageLength); - compareStringTag(exifInterface, ExifInterface.TAG_IMAGE_WIDTH, expectedValue.imageWidth); - compareStringTag(exifInterface, ExifInterface.TAG_ISO, expectedValue.iso); - compareStringTag(exifInterface, ExifInterface.TAG_ORIENTATION, expectedValue.orientation); - compareStringTag(exifInterface, ExifInterface.TAG_WHITE_BALANCE, - expectedValue.whiteBalance); + assertStringTag(exifInterface, ExifInterface.TAG_GPS_TIMESTAMP, expectedValue.gpsTimestamp); + assertIntTag(exifInterface, ExifInterface.TAG_IMAGE_LENGTH, expectedValue.imageLength); + assertIntTag(exifInterface, ExifInterface.TAG_IMAGE_WIDTH, expectedValue.imageWidth); + assertStringTag(exifInterface, ExifInterface.TAG_ISO, expectedValue.iso); + assertIntTag(exifInterface, ExifInterface.TAG_ORIENTATION, expectedValue.orientation); + assertIntTag(exifInterface, ExifInterface.TAG_WHITE_BALANCE, expectedValue.whiteBalance); } private void testExifInterfaceCommon(File imageFile, ExpectedValue expectedValue) throws IOException { - // Created via path. + String verboseTag = imageFile.getName(); + + // Creates via path. ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath()); - if (VERBOSE) { - printExifTagsAndValues(imageFile.getName(), exifInterface); - } - compareWithExpectedValue(exifInterface, expectedValue); + compareWithExpectedValue(exifInterface, expectedValue, verboseTag); - // Created from an asset file. - InputStream in = mContext.getAssets().open(imageFile.getName()); - exifInterface = new ExifInterface(in); - if (VERBOSE) { - printExifTagsAndValues(imageFile.getName(), exifInterface); + // Creates from an asset file. + InputStream in = null; + try { + in = mContext.getAssets().open(imageFile.getName()); + exifInterface = new ExifInterface(in); + compareWithExpectedValue(exifInterface, expectedValue, verboseTag); + } finally { + IoUtils.closeQuietly(in); } - compareWithExpectedValue(exifInterface, expectedValue); - // Created via InputStream. + // Creates via InputStream. in = null; try { in = new BufferedInputStream(new FileInputStream(imageFile.getAbsolutePath())); exifInterface = new ExifInterface(in); - if (VERBOSE) { - printExifTagsAndValues(imageFile.getName(), exifInterface); - } - compareWithExpectedValue(exifInterface, expectedValue); + compareWithExpectedValue(exifInterface, expectedValue, verboseTag); } finally { IoUtils.closeQuietly(in); } - // Created via FileDescriptor. + // Creates via FileDescriptor. + FileDescriptor fd = null; try { - FileDescriptor fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDONLY, 0600); + fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDONLY, 0600); exifInterface = new ExifInterface(fd); - if (VERBOSE) { - printExifTagsAndValues(imageFile.getName(), exifInterface); - } - compareWithExpectedValue(exifInterface, expectedValue); + compareWithExpectedValue(exifInterface, expectedValue, verboseTag); } catch (ErrnoException e) { - e.rethrowAsIOException(); + throw e.rethrowAsIOException(); + } finally { + IoUtils.closeQuietly(fd); } } - private void testExifInterfaceForJpeg(String fileName, int typedArrayResourceId) + private void testSaveAttributes_withFileName(File imageFile, ExpectedValue expectedValue) throws IOException { - ExpectedValue expectedValue = new ExpectedValue( - getContext().getResources().obtainTypedArray(typedArrayResourceId)); - File imageFile = new File(Environment.getExternalStorageDirectory(), fileName); + String verboseTag = imageFile.getName(); - // Test for reading from various inputs. - testExifInterfaceCommon(imageFile, expectedValue); + ExifInterface exifInterface = new ExifInterface(imageFile.getAbsolutePath()); + exifInterface.saveAttributes(); + exifInterface = new ExifInterface(imageFile.getAbsolutePath()); + compareWithExpectedValue(exifInterface, expectedValue, verboseTag); - // Test for saving attributes. - ExifInterface exifInterface; + // Test for modifying one attribute. + String backupValue = exifInterface.getAttribute(ExifInterface.TAG_MAKE); + exifInterface.setAttribute(ExifInterface.TAG_MAKE, "abc"); + exifInterface.saveAttributes(); + exifInterface = new ExifInterface(imageFile.getAbsolutePath()); + assertEquals("abc", exifInterface.getAttribute(ExifInterface.TAG_MAKE)); + // Restore the backup value. + exifInterface.setAttribute(ExifInterface.TAG_MAKE, backupValue); + exifInterface.saveAttributes(); + exifInterface = new ExifInterface(imageFile.getAbsolutePath()); + compareWithExpectedValue(exifInterface, expectedValue, verboseTag); + } + + private void testSaveAttributes_withFileDescriptor(File imageFile, ExpectedValue expectedValue) + throws IOException { + String verboseTag = imageFile.getName(); + + FileDescriptor fd = null; try { - FileDescriptor fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDWR, 0600); + fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDWR, 0600); + ExifInterface exifInterface = new ExifInterface(fd); + exifInterface.saveAttributes(); + Os.lseek(fd, 0, OsConstants.SEEK_SET); exifInterface = new ExifInterface(fd); + compareWithExpectedValue(exifInterface, expectedValue, verboseTag); + + // Test for modifying one attribute. + String backupValue = exifInterface.getAttribute(ExifInterface.TAG_MAKE); + exifInterface.setAttribute(ExifInterface.TAG_MAKE, "abc"); exifInterface.saveAttributes(); - fd = Os.open(imageFile.getAbsolutePath(), OsConstants.O_RDWR, 0600); + Os.lseek(fd, 0, OsConstants.SEEK_SET); exifInterface = new ExifInterface(fd); - if (VERBOSE) { - printExifTagsAndValues(fileName, exifInterface); - } - compareWithExpectedValue(exifInterface, expectedValue); + assertEquals("abc", exifInterface.getAttribute(ExifInterface.TAG_MAKE)); + // Restore the backup value. + exifInterface.setAttribute(ExifInterface.TAG_MAKE, backupValue); + exifInterface.saveAttributes(); + Os.lseek(fd, 0, OsConstants.SEEK_SET); + exifInterface = new ExifInterface(fd); + compareWithExpectedValue(exifInterface, expectedValue, verboseTag); } catch (ErrnoException e) { - e.rethrowAsIOException(); + throw e.rethrowAsIOException(); + } finally { + IoUtils.closeQuietly(fd); } + } - // Test for modifying one attribute. - exifInterface = new ExifInterface(imageFile.getAbsolutePath()); - exifInterface.setAttribute(ExifInterface.TAG_MAKE, "abc"); - exifInterface.saveAttributes(); - exifInterface = new ExifInterface(imageFile.getAbsolutePath()); - if (VERBOSE) { - printExifTagsAndValues(fileName, exifInterface); + private void testSaveAttributes_withInputStream(File imageFile, ExpectedValue expectedValue) + throws IOException { + InputStream in = null; + try { + in = getContext().getAssets().open(imageFile.getName()); + ExifInterface exifInterface = new ExifInterface(in); + exifInterface.saveAttributes(); + } catch (UnsupportedOperationException e) { + // Expected. saveAttributes is not supported with an ExifInterface object which was + // created with InputStream. + return; + } finally { + IoUtils.closeQuietly(in); } - assertEquals("abc", exifInterface.getAttribute(ExifInterface.TAG_MAKE)); + fail("Should not reach here!"); + } + + private void testExifInterfaceForJpeg(String fileName, int typedArrayResourceId) + throws IOException { + ExpectedValue expectedValue = new ExpectedValue( + getContext().getResources().obtainTypedArray(typedArrayResourceId)); + File imageFile = new File(Environment.getExternalStorageDirectory(), fileName); + + // Test for reading from various inputs. + testExifInterfaceCommon(imageFile, expectedValue); + + // Test for saving attributes. + testSaveAttributes_withFileName(imageFile, expectedValue); + testSaveAttributes_withFileDescriptor(imageFile, expectedValue); + testSaveAttributes_withInputStream(imageFile, expectedValue); } private void testExifInterfaceForRaw(String fileName, int typedArrayResourceId) diff --git a/packages/DocumentsUI/perf-tests/Android.mk b/packages/DocumentsUI/perf-tests/Android.mk index 919fc560951b..5ebf85f74d2a 100644 --- a/packages/DocumentsUI/perf-tests/Android.mk +++ b/packages/DocumentsUI/perf-tests/Android.mk @@ -2,8 +2,8 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests -#LOCAL_SDK_VERSION := current +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_SRC_FILES := $(call all-java-files-under, src) \ $(call all-java-files-under, ../tests/src/com/android/documentsui/bots) \ ../tests/src/com/android/documentsui/ActivityTest.java \ diff --git a/packages/DocumentsUI/perf-tests/res/raw/earth_small.jpg b/packages/DocumentsUI/perf-tests/res/raw/earth_small.jpg Binary files differnew file mode 100644 index 000000000000..dd2da3e9081b --- /dev/null +++ b/packages/DocumentsUI/perf-tests/res/raw/earth_small.jpg diff --git a/packages/DocumentsUI/perf-tests/src/com/android/documentsui/StressProvider.java b/packages/DocumentsUI/perf-tests/src/com/android/documentsui/StressProvider.java index 6147a7187de7..f9b06f804bac 100644 --- a/packages/DocumentsUI/perf-tests/src/com/android/documentsui/StressProvider.java +++ b/packages/DocumentsUI/perf-tests/src/com/android/documentsui/StressProvider.java @@ -18,9 +18,11 @@ package com.android.documentsui; import android.content.Context; import android.content.pm.ProviderInfo; +import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.database.MatrixCursor.RowBuilder; import android.database.MatrixCursor; +import android.graphics.Point; import android.os.CancellationSignal; import android.os.FileUtils; import android.os.ParcelFileDescriptor; @@ -31,6 +33,7 @@ import android.provider.DocumentsProvider; import java.io.File; import java.io.FileNotFoundException; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -102,7 +105,14 @@ public class StressProvider extends DocumentsProvider { children = new ArrayList<StubDocument>(); mChildDocuments.put(STRESS_ROOT_2_DOC_ID, children); for (int i = 0; i < STRESS_ROOT_2_ITEMS; i++) { - document = StubDocument.createFile(STRESS_ROOT_1_ITEMS + i); + try { + document = StubDocument.createFile( + getContext(), MIME_TYPE_IMAGE, + com.android.documentsui.perftests.R.raw.earth_small, + STRESS_ROOT_1_ITEMS + i); + } catch (IOException e) { + return false; + } mDocuments.put(document.id, document); children.add(document); } @@ -151,6 +161,14 @@ public class StressProvider extends DocumentsProvider { } @Override + public AssetFileDescriptor openDocumentThumbnail(String docId, Point sizeHint, + CancellationSignal signal) + throws FileNotFoundException { + final StubDocument document = mDocuments.get(docId); + return getContext().getResources().openRawResourceFd(document.thumbnail); + } + + @Override public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal) throws FileNotFoundException { @@ -171,7 +189,8 @@ public class StressProvider extends DocumentsProvider { row.add(Document.COLUMN_DISPLAY_NAME, document.id); row.add(Document.COLUMN_SIZE, document.size); row.add(Document.COLUMN_MIME_TYPE, document.mimeType); - row.add(Document.COLUMN_FLAGS, 0); + row.add(Document.COLUMN_FLAGS, + document.thumbnail != -1 ? Document.FLAG_SUPPORTS_THUMBNAIL : 0); row.add(Document.COLUMN_LAST_MODIFIED, document.lastModified); } @@ -184,28 +203,32 @@ public class StressProvider extends DocumentsProvider { final String id; final int size; final long lastModified; + final int thumbnail; - private StubDocument(String mimeType, String id, int size, long lastModified) { + private StubDocument(String mimeType, String id, int size, long lastModified, + int thumbnail) { this.mimeType = mimeType; this.id = id; this.size = size; this.lastModified = lastModified; + this.thumbnail = thumbnail; } public static StubDocument createDirectory(int index) { return new StubDocument( DocumentsContract.Document.MIME_TYPE_DIR, createRandomId(index), 0, - createRandomTime(index)); + createRandomTime(index), -1); } public static StubDocument createDirectory(String id) { - return new StubDocument(DocumentsContract.Document.MIME_TYPE_DIR, id, 0, 0); + return new StubDocument(DocumentsContract.Document.MIME_TYPE_DIR, id, 0, 0, -1); } - public static StubDocument createFile(int index) { + public static StubDocument createFile(Context context, String mimeType, int thumbnail, + int index) throws IOException { return new StubDocument( - MIME_TYPE_IMAGE, createRandomId(index), createRandomSize(index), - createRandomTime(index)); + mimeType, createRandomId(index), createRandomSize(index), + createRandomTime(index), thumbnail); } private static String createRandomId(int index) { diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java index 2d3935b6c9e7..99145b7bda0a 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java +++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java @@ -178,6 +178,8 @@ public final class RemotePrintDocument { } if (mState == STATE_FAILED) { Log.w(LOG_TAG, "Failed before start."); + } else if (mState == STATE_DESTROYED) { + Log.w(LOG_TAG, "Destroyed before start."); } else { if (mState != STATE_INITIAL) { throw new IllegalStateException("Cannot start in state:" + stateToString(mState)); @@ -267,7 +269,7 @@ public final class RemotePrintDocument { } if (mState != STATE_STARTED && mState != STATE_UPDATED && mState != STATE_FAILED && mState != STATE_CANCELING - && mState != STATE_CANCELED) { + && mState != STATE_CANCELED && mState != STATE_DESTROYED) { throw new IllegalStateException("Cannot finish in state:" + stateToString(mState)); } diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index da9c48a2a5ee..d10df02a3f2f 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -32,6 +32,7 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; import android.net.INetworkStatsService; import android.net.InterfaceConfiguration; import android.net.LinkAddress; @@ -40,6 +41,7 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkRequest; +import android.net.NetworkState; import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.wifi.WifiManager; @@ -88,7 +90,7 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class Tethering extends BaseNetworkObserver { - private Context mContext; + private final Context mContext; private final static String TAG = "Tethering"; private final static boolean DBG = false; private final static boolean VDBG = false; @@ -100,7 +102,7 @@ public class Tethering extends BaseNetworkObserver { private Collection<Integer> mUpstreamIfaceTypes; // used to synchronize public access to members - private Object mPublicSync; + private final Object mPublicSync; private static final Integer MOBILE_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE); private static final Integer HIPRI_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE_HIPRI); @@ -112,7 +114,7 @@ public class Tethering extends BaseNetworkObserver { private final INetworkManagementService mNMService; private final INetworkStatsService mStatsService; - private Looper mLooper; + private final Looper mLooper; private HashMap<String, TetherInterfaceSM> mIfaces; // all tethered/tetherable ifaces @@ -143,7 +145,9 @@ public class Tethering extends BaseNetworkObserver { private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8"; private static final String DNS_DEFAULT_SERVER2 = "8.8.4.4"; - private StateMachine mTetherMasterSM; + private final StateMachine mTetherMasterSM; + private final UpstreamNetworkMonitor mUpstreamNetworkMonitor; + private String mCurrentUpstreamIface; private Notification.Builder mTetheredNotificationBuilder; private int mLastNotificationId; @@ -167,6 +171,8 @@ public class Tethering extends BaseNetworkObserver { mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper); mTetherMasterSM.start(); + mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(); + mStateReceiver = new StateReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction(UsbManager.ACTION_USB_STATE); @@ -505,7 +511,7 @@ public class Tethering extends BaseNetworkObserver { }; // The following is necessary to avoid unmarshalling issues when sending the receiver - // across proccesses. + // across processes. Parcel parcel = Parcel.obtain(); rr.writeToParcel(parcel,0); parcel.setDataPosition(0); @@ -559,6 +565,7 @@ public class Tethering extends BaseNetworkObserver { } } } + public int tether(String iface) { if (DBG) Log.d(TAG, "Tethering " + iface); TetherInterfaceSM sm = null; @@ -1371,15 +1378,115 @@ public class Tethering extends BaseNetworkObserver { } + /** + * A NetworkCallback class that relays information of interest to the + * tethering master state machine thread for subsequent processing. + */ + class UpstreamNetworkCallback extends NetworkCallback { + @Override + public void onLinkPropertiesChanged(Network network, LinkProperties newLp) { + mTetherMasterSM.sendMessage( + TetherMasterSM.EVENT_UPSTREAM_LINKPROPERTIES_CHANGED, + new NetworkState(null, newLp, null, network, null, null)); + } + + @Override + public void onLost(Network network) { + mTetherMasterSM.sendMessage(TetherMasterSM.EVENT_UPSTREAM_LOST, network); + } + } + + /** + * A class to centralize all the network and link properties information + * pertaining to the current and any potential upstream network. + * + * Calling #start() registers two callbacks: one to track the system default + * network and a second to specifically observe TYPE_MOBILE_DUN networks. + * + * The methods and data members of this class are only to be accessed and + * modified from the tethering master state machine thread. Any other + * access semantics would necessitate the addition of locking. + * + * TODO: Investigate whether more "upstream-specific" logic/functionality + * could/should be moved here. + */ + class UpstreamNetworkMonitor { + final HashMap<Network, NetworkState> mNetworkMap = new HashMap(); + NetworkCallback mDefaultNetworkCallback; + NetworkCallback mDunTetheringCallback; + + void start() { + stop(); + + mDefaultNetworkCallback = new UpstreamNetworkCallback(); + getConnectivityManager().registerDefaultNetworkCallback(mDefaultNetworkCallback); + + final NetworkRequest dunTetheringRequest = new NetworkRequest.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN) + .build(); + mDunTetheringCallback = new UpstreamNetworkCallback(); + getConnectivityManager().registerNetworkCallback( + dunTetheringRequest, mDunTetheringCallback); + } + + void stop() { + if (mDefaultNetworkCallback != null) { + getConnectivityManager().unregisterNetworkCallback(mDefaultNetworkCallback); + mDefaultNetworkCallback = null; + } + + if (mDunTetheringCallback != null) { + getConnectivityManager().unregisterNetworkCallback(mDunTetheringCallback); + mDunTetheringCallback = null; + } + + mNetworkMap.clear(); + } + + // Returns true if these updated LinkProperties pertain to the current + // upstream network interface, false otherwise (or if there is not + // currently any upstream tethering interface). + boolean processLinkPropertiesChanged(NetworkState networkState) { + if (networkState == null || + networkState.network == null || + networkState.linkProperties == null) { + return false; + } + + mNetworkMap.put(networkState.network, networkState); + + if (mCurrentUpstreamIface != null) { + for (String ifname : networkState.linkProperties.getAllInterfaceNames()) { + if (mCurrentUpstreamIface.equals(ifname)) { + return true; + } + } + } + return false; + } + + void processNetworkLost(Network network) { + if (network != null) { + mNetworkMap.remove(network); + } + } + } + class TetherMasterSM extends StateMachine { // an interface SM has requested Tethering - static final int CMD_TETHER_MODE_REQUESTED = 1; + static final int CMD_TETHER_MODE_REQUESTED = 1; // an interface SM has unrequested Tethering - static final int CMD_TETHER_MODE_UNREQUESTED = 2; + static final int CMD_TETHER_MODE_UNREQUESTED = 2; // upstream connection change - do the right thing - static final int CMD_UPSTREAM_CHANGED = 3; + static final int CMD_UPSTREAM_CHANGED = 3; // we don't have a valid upstream conn, check again after a delay - static final int CMD_RETRY_UPSTREAM = 4; + static final int CMD_RETRY_UPSTREAM = 4; + // Events from NetworkCallbacks that we process on the master state + // machine thread on behalf of the UpstreamNetworkMonitor. + static final int EVENT_UPSTREAM_LINKPROPERTIES_CHANGED = 5; + static final int EVENT_UPSTREAM_LOST = 6; // This indicates what a timeout event relates to. A state that // sends itself a delayed timeout event and handles incoming timeout events @@ -1399,9 +1506,7 @@ public class Tethering extends BaseNetworkObserver { private ArrayList<TetherInterfaceSM> mNotifyList; private int mMobileApnReserved = ConnectivityManager.TYPE_NONE; - private ConnectivityManager.NetworkCallback mMobileUpstreamCallback; - - private String mUpstreamIfaceName = null; + private NetworkCallback mMobileUpstreamCallback; private static final int UPSTREAM_SETTLE_TIME_MS = 10000; @@ -1430,8 +1535,6 @@ public class Tethering extends BaseNetworkObserver { } class TetherMasterUtilState extends State { - protected final static boolean WAIT_FOR_NETWORK_TO_SETTLE = false; - @Override public boolean processMessage(Message m) { return false; @@ -1461,27 +1564,27 @@ public class Tethering extends BaseNetworkObserver { return false; } - NetworkRequest.Builder builder = new NetworkRequest.Builder() + final NetworkRequest.Builder builder = new NetworkRequest.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); if (apnType == ConnectivityManager.TYPE_MOBILE_DUN) { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN) - .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); + builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN); } else { builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); } - NetworkRequest mobileUpstreamRequest = builder.build(); - // Other mechanisms notice network and interface changes and act upon them. - // TODO, imminently: replace with a proper NetworkCallback-based scheme. - // + final NetworkRequest mobileUpstreamRequest = builder.build(); + + // The UpstreamNetworkMonitor's callback will be notified. + // Therefore, to avoid duplicate notifications, we only register a no-op. + mMobileUpstreamCallback = new NetworkCallback(); + // TODO: Change the timeout from 0 (no onUnavailable callback) to use some // moderate callback time (once timeout callbacks are implemented). This might // be useful for updating some UI. Additionally, we should definitely log a - // message to aid in any subsequent debugging. - mMobileUpstreamCallback = new ConnectivityManager.NetworkCallback(); + // message to aid in any subsequent debugging if (DBG) Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest); getConnectivityManager().requestNetwork( mobileUpstreamRequest, mMobileUpstreamCallback, 0, apnType); - return true; } @@ -1513,6 +1616,7 @@ public class Tethering extends BaseNetworkObserver { } return true; } + protected boolean turnOffMasterTetherSettings() { try { mNMService.stopTethering(); @@ -1606,34 +1710,41 @@ public class Tethering extends BaseNetworkObserver { } if (iface != null) { - String[] dnsServers = mDefaultDnsServers; - Collection<InetAddress> dnses = linkProperties.getDnsServers(); - if (dnses != null && !dnses.isEmpty()) { - // TODO: remove this invocation of NetworkUtils.makeStrings(). - dnsServers = NetworkUtils.makeStrings(dnses); - } - try { - Network network = getConnectivityManager().getNetworkForType(upType); - if (network == null) { - Log.e(TAG, "No Network for upstream type " + upType + "!"); - } - if (VDBG) { - Log.d(TAG, "Setting DNS forwarders: Network=" + network + - ", dnsServers=" + Arrays.toString(dnsServers)); - } - mNMService.setDnsForwarders(network, dnsServers); - } catch (Exception e) { - Log.e(TAG, "Setting DNS forwarders failed!"); - transitionTo(mSetDnsForwardersErrorState); + Network network = getConnectivityManager().getNetworkForType(upType); + if (network == null) { + Log.e(TAG, "No Network for upstream type " + upType + "!"); } + setDnsForwarders(network, linkProperties); } } notifyTetheredOfNewUpstreamIface(iface); } + protected void setDnsForwarders(final Network network, final LinkProperties lp) { + String[] dnsServers = mDefaultDnsServers; + final Collection<InetAddress> dnses = lp.getDnsServers(); + // TODO: Properly support the absence of DNS servers. + if (dnses != null && !dnses.isEmpty()) { + // TODO: remove this invocation of NetworkUtils.makeStrings(). + dnsServers = NetworkUtils.makeStrings(dnses); + } + if (VDBG) { + Log.d(TAG, "Setting DNS forwarders: Network=" + network + + ", dnsServers=" + Arrays.toString(dnsServers)); + } + try { + mNMService.setDnsForwarders(network, dnsServers); + } catch (Exception e) { + // TODO: Investigate how this can fail and what exactly + // happens if/when such failures occur. + Log.e(TAG, "Setting DNS forwarders failed!"); + transitionTo(mSetDnsForwardersErrorState); + } + } + protected void notifyTetheredOfNewUpstreamIface(String ifaceName) { if (DBG) Log.d(TAG, "notifying tethered with iface =" + ifaceName); - mUpstreamIfaceName = ifaceName; + mCurrentUpstreamIface = ifaceName; for (TetherInterfaceSM sm : mNotifyList) { sm.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED, ifaceName); @@ -1772,20 +1883,23 @@ public class Tethering extends BaseNetworkObserver { } class TetherModeAliveState extends TetherMasterUtilState { - boolean mTryCell = !WAIT_FOR_NETWORK_TO_SETTLE; + boolean mTryCell = true; @Override public void enter() { + // TODO: examine if we should check the return value. turnOnMasterTetherSettings(); // may transition us out startListeningForSimChanges(); + mUpstreamNetworkMonitor.start(); - mTryCell = !WAIT_FOR_NETWORK_TO_SETTLE; // better try something first pass - // or crazy tests cases will fail + mTryCell = true; // better try something first pass or crazy tests cases will fail chooseUpstreamType(mTryCell); mTryCell = !mTryCell; } @Override public void exit() { + // TODO: examine if we should check the return value. turnOffUpstreamMobileConnection(); + mUpstreamNetworkMonitor.stop(); stopListeningForSimChanges(); notifyTetheredOfNewUpstreamIface(null); } @@ -1799,7 +1913,7 @@ public class Tethering extends BaseNetworkObserver { if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); mNotifyList.add(who); who.sendMessage(TetherInterfaceSM.CMD_TETHER_CONNECTION_CHANGED, - mUpstreamIfaceName); + mCurrentUpstreamIface); break; case CMD_TETHER_MODE_UNREQUESTED: who = (TetherInterfaceSM)message.obj; @@ -1823,7 +1937,7 @@ public class Tethering extends BaseNetworkObserver { break; case CMD_UPSTREAM_CHANGED: // need to try DUN immediately if Wifi goes down - mTryCell = !WAIT_FOR_NETWORK_TO_SETTLE; + mTryCell = true; chooseUpstreamType(mTryCell); mTryCell = !mTryCell; break; @@ -1831,6 +1945,24 @@ public class Tethering extends BaseNetworkObserver { chooseUpstreamType(mTryCell); mTryCell = !mTryCell; break; + case EVENT_UPSTREAM_LINKPROPERTIES_CHANGED: + NetworkState state = (NetworkState) message.obj; + if (mUpstreamNetworkMonitor.processLinkPropertiesChanged(state)) { + setDnsForwarders(state.network, state.linkProperties); + } else if (mCurrentUpstreamIface == null) { + // If we have no upstream interface, try to run through upstream + // selection again. If, for example, IPv4 connectivity has shown up + // after IPv6 (e.g., 464xlat became available) we want the chance to + // notice and act accordingly. + chooseUpstreamType(false); + } + break; + case EVENT_UPSTREAM_LOST: + // TODO: Re-evaluate possible upstreams. Currently upstream reevaluation + // is triggered via received CONNECTIVITY_ACTION broadcasts that result + // in being passed a TetherMasterSM.CMD_UPSTREAM_CHANGED. + mUpstreamNetworkMonitor.processNetworkLost((Network) message.obj); + break; default: retValue = false; break; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 442643a976e2..fdbbd853a6ec 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -6926,8 +6926,8 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - public void extractPackagesIfNeeded() { - enforceSystemOrRoot("Only the system can request package extraction"); + public void updatePackagesIfNeeded() { + enforceSystemOrRoot("Only the system can request package update"); // We need to re-extract after an OTA. boolean causeUpgrade = isUpgrade(); @@ -6958,15 +6958,6 @@ public class PackageManagerService extends IPackageManager.Stub { Log.i(TAG, "Extracting app " + curr + " of " + total + ": " + pkg.packageName); } - if (!isFirstBoot()) { - try { - ActivityManagerNative.getDefault().showBootMessage( - mContext.getResources().getString(R.string.android_upgrading_apk, - curr, total), true); - } catch (RemoteException e) { - } - } - if (PackageDexOptimizer.canOptimizePackage(pkg)) { // If the cache was pruned, any compiled odex files will likely be out of date // and would have to be patched (would be SELF_PATCHOAT, which is deprecated). @@ -7247,9 +7238,9 @@ public class PackageManagerService extends IPackageManager.Stub { private void deleteProfilesLI(PackageParser.Package pkg, boolean destroy) { try { if (destroy) { - mInstaller.clearAppProfiles(pkg.packageName); - } else { mInstaller.destroyAppProfiles(pkg.packageName); + } else { + mInstaller.clearAppProfiles(pkg.packageName); } } catch (InstallerException ex) { Log.e(TAG, "Could not delete profiles for package " + pkg.packageName); @@ -14521,7 +14512,6 @@ public class PackageManagerService extends IPackageManager.Stub { if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageX: pkg=" + packageName + " user=" + userId); res = deletePackageLI(packageName, removeForUser, true, allUsers, flags | REMOVE_CHATTY, info, true, null); - deleteProfilesLI(packageName, /*destroy*/ true); synchronized (mPackages) { if (res) { mEphemeralApplicationRegistry.onPackageUninstalledLPw(uninstalledPs.pkg); diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index f9414328885c..5916202a0bdb 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -55,6 +55,7 @@ class ShortcutPackage extends ShortcutPackageItem { private static final String ATTR_ID = "id"; private static final String ATTR_ACTIVITY = "activity"; private static final String ATTR_TITLE = "title"; + private static final String ATTR_TEXT = "text"; private static final String ATTR_INTENT = "intent"; private static final String ATTR_WEIGHT = "weight"; private static final String ATTR_TIMESTAMP = "timestamp"; @@ -439,6 +440,7 @@ class ShortcutPackage extends ShortcutPackageItem { ShortcutService.writeAttr(out, ATTR_ACTIVITY, si.getActivityComponent()); // writeAttr(out, "icon", si.getIcon()); // We don't save it. ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle()); + ShortcutService.writeAttr(out, ATTR_TEXT, si.getText()); ShortcutService.writeAttr(out, ATTR_INTENT, si.getIntentNoExtras()); ShortcutService.writeAttr(out, ATTR_WEIGHT, si.getWeight()); ShortcutService.writeAttr(out, ATTR_TIMESTAMP, @@ -515,6 +517,7 @@ class ShortcutPackage extends ShortcutPackageItem { ComponentName activityComponent; // Icon icon; String title; + String text; Intent intent; PersistableBundle intentPersistableExtras = null; int weight; @@ -528,6 +531,7 @@ class ShortcutPackage extends ShortcutPackageItem { activityComponent = ShortcutService.parseComponentNameAttribute(parser, ATTR_ACTIVITY); title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE); + text = ShortcutService.parseStringAttribute(parser, ATTR_TEXT); intent = ShortcutService.parseIntentAttribute(parser, ATTR_INTENT); weight = (int) ShortcutService.parseLongAttribute(parser, ATTR_WEIGHT); lastChangedTimestamp = ShortcutService.parseLongAttribute(parser, ATTR_TIMESTAMP); @@ -559,7 +563,7 @@ class ShortcutPackage extends ShortcutPackageItem { throw ShortcutService.throwForInvalidTag(depth, tag); } return new ShortcutInfo( - id, packageName, activityComponent, /* icon =*/ null, title, intent, + id, packageName, activityComponent, /* icon =*/ null, title, text, intent, intentPersistableExtras, weight, extras, lastChangedTimestamp, flags, iconRes, bitmapPath); } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 4a00ebd713bb..dbbaa5ebf75f 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -319,7 +319,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { */ @Override public void disable2(int what, IBinder token, String pkg) { - disableForUser(what, token, pkg, mCurrentUserId); + disable2ForUser(what, token, pkg, mCurrentUserId); } /** diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index b2cd69d9f371..15a10e92eff7 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5696,26 +5696,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override - public boolean setDeviceOwnerLockScreenInfo(ComponentName who, String info) { + public void setDeviceOwnerLockScreenInfo(ComponentName who, CharSequence info) { Preconditions.checkNotNull(who, "ComponentName is null"); if (!mHasFeature) { - return false; + return; } synchronized (this) { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); long token = mInjector.binderClearCallingIdentity(); try { - mLockPatternUtils.setDeviceOwnerInfo(info); + mLockPatternUtils.setDeviceOwnerInfo(info != null ? info.toString() : null); } finally { mInjector.binderRestoreCallingIdentity(token); } - return true; } } @Override - public String getDeviceOwnerLockScreenInfo() { + public CharSequence getDeviceOwnerLockScreenInfo() { return mLockPatternUtils.getDeviceOwnerInfo(); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 0a4effb26249..7b44f98e6d1a 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -697,19 +697,19 @@ public final class SystemServer { // as appropriate. mSystemServiceManager.startService(UiModeManagerService.class); - Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "PerformFstrimIfNeeded"); + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "UpdatePackagesIfNeeded"); try { - mPackageManagerService.performFstrimIfNeeded(); + mPackageManagerService.updatePackagesIfNeeded(); } catch (Throwable e) { - reportWtf("performing fstrim", e); + reportWtf("update packages", e); } Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); - Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "ExtractPackagesIfNeeded"); + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "PerformFstrimIfNeeded"); try { - mPackageManagerService.extractPackagesIfNeeded(); + mPackageManagerService.performFstrimIfNeeded(); } catch (Throwable e) { - reportWtf("extract packages", e); + reportWtf("performing fstrim", e); } Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java index eb16a1db5876..c44ffa481f8b 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutInfoTest.java @@ -16,9 +16,16 @@ */ package com.android.server.pm; +import android.content.ComponentName; +import android.content.Intent; import android.content.pm.ShortcutInfo; +import android.graphics.drawable.Icon; +import android.os.Bundle; +import android.os.Parcel; +import android.os.PersistableBundle; import android.test.AndroidTestCase; +import com.android.internal.util.Preconditions; import com.android.server.testutis.TestUtils; /** @@ -33,12 +40,232 @@ import com.android.server.testutis.TestUtils; */ public class ShortcutInfoTest extends AndroidTestCase { - public void testNoId() { + public void testMissingMandatoryFields() { TestUtils.assertExpectException( IllegalArgumentException.class, "ID must be provided", () -> new ShortcutInfo.Builder(mContext).build()); + TestUtils.assertExpectException( + IllegalArgumentException.class, + "title must be provided", + () -> new ShortcutInfo.Builder(mContext).setId("id").build() + .enforceMandatoryFields()); + TestUtils.assertExpectException( + NullPointerException.class, + "Intent must be provided", + () -> new ShortcutInfo.Builder(mContext).setId("id").setTitle("x").build() + .enforceMandatoryFields()); } - // TODO Add more tests. + private ShortcutInfo parceled(ShortcutInfo si) { + Parcel p = Parcel.obtain(); + p.writeParcelable(si, 0); + p.setDataPosition(0); + ShortcutInfo si2 = p.readParcelable(getClass().getClassLoader()); + p.recycle(); + return si2; + } + + private Intent makeIntent(String action, Object... bundleKeysAndValues) { + final Intent intent = new Intent(action); + intent.replaceExtras(ShortcutManagerTest.makeBundle(bundleKeysAndValues)); + return intent; + } + + public void testParcel() { + ShortcutInfo si = parceled(new ShortcutInfo.Builder(getContext()) + .setId("id") + .setTitle("title") + .setIntent(makeIntent("action")) + .build()); + assertEquals(getContext().getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals("title", si.getTitle()); + assertEquals("action", si.getIntent().getAction()); + + PersistableBundle pb = new PersistableBundle(); + pb.putInt("k", 1); + + si = new ShortcutInfo.Builder(getContext()) + .setId("id") + .setActivityComponent(new ComponentName("a", "b")) + .setIcon(Icon.createWithContentUri("content://a.b.c/")) + .setTitle("title") + .setText("text") + .setIntent(makeIntent("action", "key", "val")) + .setWeight(123) + .setExtras(pb) + .build(); + si.addFlags(ShortcutInfo.FLAG_PINNED); + si.setBitmapPath("abc"); + si.setIconResourceId(456); + + si = parceled(si); + + assertEquals(getContext().getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(new ComponentName("a", "b"), si.getActivityComponent()); + assertEquals("content://a.b.c/", si.getIcon().getUriString()); + assertEquals("title", si.getTitle()); + assertEquals("text", si.getText()); + assertEquals("action", si.getIntent().getAction()); + assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(123, si.getWeight()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertEquals("abc", si.getBitmapPath()); + assertEquals(456, si.getIconResourceId()); + } + + public void testClone() { + PersistableBundle pb = new PersistableBundle(); + pb.putInt("k", 1); + ShortcutInfo sorig = new ShortcutInfo.Builder(getContext()) + .setId("id") + .setActivityComponent(new ComponentName("a", "b")) + .setIcon(Icon.createWithContentUri("content://a.b.c/")) + .setTitle("title") + .setText("text") + .setIntent(makeIntent("action", "key", "val")) + .setWeight(123) + .setExtras(pb) + .build(); + sorig.addFlags(ShortcutInfo.FLAG_PINNED); + sorig.setBitmapPath("abc"); + sorig.setIconResourceId(456); + + ShortcutInfo si = sorig.clone(/* clone flags*/ 0); + + assertEquals(getContext().getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(new ComponentName("a", "b"), si.getActivityComponent()); + assertEquals("content://a.b.c/", si.getIcon().getUriString()); + assertEquals("title", si.getTitle()); + assertEquals("text", si.getText()); + assertEquals("action", si.getIntent().getAction()); + assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(123, si.getWeight()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertEquals("abc", si.getBitmapPath()); + assertEquals(456, si.getIconResourceId()); + + si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR); + + assertEquals(getContext().getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(new ComponentName("a", "b"), si.getActivityComponent()); + assertEquals(null, si.getIcon()); + assertEquals("title", si.getTitle()); + assertEquals("text", si.getText()); + assertEquals("action", si.getIntent().getAction()); + assertEquals("val", si.getIntent().getStringExtra("key")); + assertEquals(123, si.getWeight()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertEquals(null, si.getBitmapPath()); + assertEquals(0, si.getIconResourceId()); + + si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER); + + assertEquals(getContext().getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(new ComponentName("a", "b"), si.getActivityComponent()); + assertEquals(null, si.getIcon()); + assertEquals("title", si.getTitle()); + assertEquals("text", si.getText()); + assertEquals(null, si.getIntent()); + assertEquals(123, si.getWeight()); + assertEquals(1, si.getExtras().getInt("k")); + + assertEquals(ShortcutInfo.FLAG_PINNED, si.getFlags()); + assertEquals(null, si.getBitmapPath()); + assertEquals(0, si.getIconResourceId()); + + si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO); + + assertEquals(getContext().getPackageName(), si.getPackageName()); + assertEquals("id", si.getId()); + assertEquals(null, si.getActivityComponent()); + assertEquals(null, si.getIcon()); + assertEquals(null, si.getTitle()); + assertEquals(null, si.getText()); + assertEquals(null, si.getIntent()); + assertEquals(0, si.getWeight()); + assertEquals(null, si.getExtras()); + + assertEquals(ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_KEY_FIELDS_ONLY, si.getFlags()); + assertEquals(null, si.getBitmapPath()); + assertEquals(0, si.getIconResourceId()); + } + + + public void testCopyNonNullFieldsFrom() { + PersistableBundle pb = new PersistableBundle(); + pb.putInt("k", 1); + ShortcutInfo sorig = new ShortcutInfo.Builder(getContext()) + .setId("id") + .setActivityComponent(new ComponentName("a", "b")) + .setIcon(Icon.createWithContentUri("content://a.b.c/")) + .setTitle("title") + .setText("text") + .setIntent(makeIntent("action", "key", "val")) + .setWeight(123) + .setExtras(pb) + .build(); + sorig.addFlags(ShortcutInfo.FLAG_PINNED); + sorig.setBitmapPath("abc"); + sorig.setIconResourceId(456); + + ShortcutInfo si; + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id") + .setActivityComponent(new ComponentName("x", "y")).build()); + assertEquals(new ComponentName("x", "y"), si.getActivityComponent()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id") + .setIcon(Icon.createWithContentUri("content://x.y.z/")).build()); + assertEquals("content://x.y.z/", si.getIcon().getUriString()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id") + .setTitle("xyz").build()); + assertEquals("xyz", si.getTitle()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id") + .setText("xxx").build()); + assertEquals("xxx", si.getText()); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id") + .setIntent(makeIntent("action2")).build()); + assertEquals("action2", si.getIntent().getAction()); + assertEquals(null, si.getIntent().getStringExtra("key")); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id") + .setIntent(makeIntent("action3", "key", "x")).build()); + assertEquals("action3", si.getIntent().getAction()); + assertEquals("x", si.getIntent().getStringExtra("key")); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id") + .setWeight(999).build()); + assertEquals(999, si.getWeight()); + + + PersistableBundle pb2 = new PersistableBundle(); + pb2.putInt("x", 99); + + si = sorig.clone(/* flags=*/ 0); + si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getContext()).setId("id") + .setExtras(pb2).build()); + assertEquals(99, si.getExtras().getInt("x")); + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java index f034d55d9736..5d2924283ef7 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java @@ -646,7 +646,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { runTestOnUiThread(() -> {}); } - private static Bundle makeBundle(Object... keysAndValues) { + public static Bundle makeBundle(Object... keysAndValues) { Preconditions.checkState((keysAndValues.length % 2) == 0); if (keysAndValues.length == 0) { diff --git a/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java b/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java index 52e8f37f0b6c..d2a44841ee0d 100644 --- a/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java +++ b/services/tests/servicestests/src/com/android/server/testutis/TestUtils.java @@ -24,19 +24,14 @@ public class TestUtils { } public static void assertExpectException(Class<? extends Throwable> expectedExceptionType, - Runnable r) { - assertExpectException(expectedExceptionType, null, r); - } - - public static void assertExpectException(Class<? extends Throwable> expectedExceptionType, String expectedExceptionMessageRegex, Runnable r) { try { r.run(); - Assert.fail("Expected exception type " + expectedExceptionType.getClass().getName() + Assert.fail("Expected exception type " + expectedExceptionType.getName() + " was not thrown"); } catch (Throwable e) { Assert.assertTrue( - "Expected exception type was " + expectedExceptionType.getClass().getName() + "Expected exception type was " + expectedExceptionType.getName() + " but caught " + e.getClass().getName(), expectedExceptionType.isAssignableFrom(e.getClass())); if (expectedExceptionMessageRegex != null) { |