diff options
Diffstat (limited to 'core/java')
157 files changed, 5516 insertions, 1862 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 9bbb4be..edebc28 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2320,7 +2320,9 @@ public class ActivityManager { * in {@link RunningAppProcessInfo}, giving you the highest importance of all the * processes that this package has code running inside of. If there are no processes * running its code, {@link RunningAppProcessInfo#IMPORTANCE_GONE} is returned. + * @hide */ + @SystemApi public int getPackageImportance(String packageName) { try { int procState = ActivityManagerNative.getDefault().getPackageProcessState(packageName); diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index a71a258..1e9bc54 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -2069,6 +2069,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case KEYGUARD_GOING_AWAY_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + keyguardGoingAway(data.readInt() != 0, data.readInt() != 0); + reply.writeNoException(); + return true; + } + case SHOULD_UP_RECREATE_TASK_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); @@ -5179,6 +5186,19 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + public void keyguardGoingAway(boolean disableWindowAnimations, + boolean keyguardGoingToNotificationShade) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(disableWindowAnimations ? 1 : 0); + data.writeInt(keyguardGoingToNotificationShade ? 1 : 0); + mRemote.transact(KEYGUARD_GOING_AWAY_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + public boolean shouldUpRecreateTask(IBinder token, String destAffinity) throws RemoteException { Parcel data = Parcel.obtain(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index f506d59..5dcbe37 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -96,7 +96,7 @@ import android.view.Window; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.renderscript.RenderScriptCacheDir; -import android.security.AndroidKeyStoreProvider; +import android.security.keystore.AndroidKeyStoreProvider; import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.ReferrerIntent; diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index b0fda9c..5e7bd0d 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -71,10 +71,7 @@ import java.io.IOException; * {@link android.content.Context#getSystemService * Context.getSystemService(Context.ALARM_SERVICE)}. */ -public class AlarmManager -{ - private static final String TAG = "AlarmManager"; - +public class AlarmManager { /** * Alarm time in {@link System#currentTimeMillis System.currentTimeMillis()} * (wall clock time in UTC), which will wake up the device when @@ -558,7 +555,93 @@ public class AlarmManager long intervalMillis, PendingIntent operation) { setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, 0, operation, null, null); } - + + /** + * Like {@link #set(int, long, PendingIntent)}, but this alarm will be allowed to execute + * even when the system is in low-power idle modes. This type of alarm must <b>only</b> + * be used for situations where it is actually required that the alarm go off while in + * idle -- a reasonable example would be for a calendar notification that should make a + * sound so the user is aware of it. These alarms can significantly impact the power use + * of the device when idle (and thus cause significant battery blame to the app scheduling + * them), so they should be used with care. + * + * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen + * out of order with any other alarms, even those from the same app. This will clearly happen + * when the device is idle (since this alarm can go off while idle, when any other alarms + * from the app will be held until later), but may also happen even when not idle.</p> + * + * <p>Regardless of the app's target SDK version, this call always allows batching of the + * alarm.</p> + * + * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP}, + * {@link #RTC}, or {@link #RTC_WAKEUP}. + * @param triggerAtMillis time in milliseconds that the alarm should go + * off, using the appropriate clock (depending on the alarm type). + * @param operation Action to perform when the alarm goes off; + * typically comes from {@link PendingIntent#getBroadcast + * IntentSender.getBroadcast()}. + * + * @see #set(int, long, PendingIntent) + * @see #setExactAndAllowWhileIdle + * @see #cancel + * @see android.content.Context#sendBroadcast + * @see android.content.Context#registerReceiver + * @see android.content.Intent#filterEquals + * @see #ELAPSED_REALTIME + * @see #ELAPSED_REALTIME_WAKEUP + * @see #RTC + * @see #RTC_WAKEUP + */ + public void setAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) { + setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE, operation, + null, null); + } + + /** + * Like {@link #setExact(int, long, PendingIntent)}, but this alarm will be allowed to execute + * even when the system is in low-power idle modes. If you don't need exact scheduling of + * the alarm but still need to execute while idle, consider using + * {@link #setAndAllowWhileIdle}. This type of alarm must <b>only</b> + * be used for situations where it is actually required that the alarm go off while in + * idle -- a reasonable example would be for a calendar notification that should make a + * sound so the user is aware of it. These alarms can significantly impact the power use + * of the device when idle (and thus cause significant battery blame to the app scheduling + * them), so they should be used with care. + * + * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen + * out of order with any other alarms, even those from the same app. This will clearly happen + * when the device is idle (since this alarm can go off while idle, when any other alarms + * from the app will be held until later), but may also happen even when not idle. + * Note that the OS will allow itself more flexibility for scheduling these alarms than + * regular exact alarms, since the application has opted into this behavior. When the + * device is idle it may take even more liberties with scheduling in order to optimize + * for battery life.</p> + * + * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP}, + * {@link #RTC}, or {@link #RTC_WAKEUP}. + * @param triggerAtMillis time in milliseconds that the alarm should go + * off, using the appropriate clock (depending on the alarm type). + * @param operation Action to perform when the alarm goes off; + * typically comes from {@link PendingIntent#getBroadcast + * IntentSender.getBroadcast()}. + * + * @see #set + * @see #setRepeating + * @see #setWindow + * @see #cancel + * @see android.content.Context#sendBroadcast + * @see android.content.Context#registerReceiver + * @see android.content.Intent#filterEquals + * @see #ELAPSED_REALTIME + * @see #ELAPSED_REALTIME_WAKEUP + * @see #RTC + * @see #RTC_WAKEUP + */ + public void setExactAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) { + setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation, + null, null); + } + /** * Remove any alarms with a matching {@link Intent}. * Any alarm, of any type, whose Intent matches this one (as defined by diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 7104185..e728971 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -227,8 +227,10 @@ public class AppOpsManager { public static final int OP_BODY_SENSORS = 56; /** @hide Read previously received cell broadcast messages. */ public static final int OP_READ_CELL_BROADCASTS = 57; + /** @hide Inject mock location into the system. */ + public static final int OP_MOCK_LOCATION = 58; /** @hide */ - public static final int _NUM_OP = 58; + public static final int _NUM_OP = 59; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -308,6 +310,9 @@ public class AppOpsManager { /** @hide Read previously received cell broadcast messages. */ public static final String OPSTR_READ_CELL_BROADCASTS = "android:read_cell_broadcasts"; + /** Inject mock location into the system. */ + public static final String OPSTR_MOCK_LOCATION + = "android:mock_location"; /** * This maps each operation to the operation that serves as the @@ -375,7 +380,8 @@ public class AppOpsManager { OP_PROCESS_OUTGOING_CALLS, OP_USE_FINGERPRINT, OP_BODY_SENSORS, - OP_READ_CELL_BROADCASTS + OP_READ_CELL_BROADCASTS, + OP_MOCK_LOCATION }; /** @@ -440,7 +446,8 @@ public class AppOpsManager { null, OPSTR_USE_FINGERPRINT, OPSTR_BODY_SENSORS, - OPSTR_READ_CELL_BROADCASTS + OPSTR_READ_CELL_BROADCASTS, + OPSTR_MOCK_LOCATION }; /** @@ -505,7 +512,8 @@ public class AppOpsManager { "PROCESS_OUTGOING_CALLS", "USE_FINGERPRINT", "BODY_SENSORS", - "READ_CELL_BROADCASTS" + "READ_CELL_BROADCASTS", + "MOCK_LOCATION" }; /** @@ -570,7 +578,8 @@ public class AppOpsManager { Manifest.permission.PROCESS_OUTGOING_CALLS, Manifest.permission.USE_FINGERPRINT, Manifest.permission.BODY_SENSORS, - Manifest.permission.READ_CELL_BROADCASTS + Manifest.permission.READ_CELL_BROADCASTS, + null }; /** @@ -636,7 +645,8 @@ public class AppOpsManager { null, // PROCESS_OUTGOING_CALLS null, // USE_FINGERPRINT null, // BODY_SENSORS - null // READ_CELL_BROADCASTS + null, // READ_CELL_BROADCASTS + null // MOCK_LOCATION }; /** @@ -701,7 +711,8 @@ public class AppOpsManager { false, // PROCESS_OUTGOING_CALLS false, // USE_FINGERPRINT false, // BODY_SENSORS - false // READ_CELL_BROADCASTS + false, // READ_CELL_BROADCASTS + false // MOCK_LOCATION }; /** @@ -765,7 +776,8 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, - AppOpsManager.MODE_ALLOWED + AppOpsManager.MODE_ALLOWED, + AppOpsManager.MODE_ERRORED // OP_MOCK_LOCATION }; /** @@ -833,6 +845,7 @@ public class AppOpsManager { false, false, false, + false, false }; diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 07eee12..04f6430 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -446,18 +446,40 @@ final class ApplicationPackageManager extends PackageManager { } @Override - public void grantPermission(String packageName, String permissionName, UserHandle user) { + public void grantRuntimePermission(String packageName, String permissionName, + UserHandle user) { + try { + mPM.grantRuntimePermission(packageName, permissionName, user.getIdentifier()); + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + } + + @Override + public void revokeRuntimePermission(String packageName, String permissionName, + UserHandle user) { + try { + mPM.revokeRuntimePermission(packageName, permissionName, user.getIdentifier()); + } catch (RemoteException e) { + throw new RuntimeException("Package manager has died", e); + } + } + + @Override + public int getPermissionFlags(String permissionName, String packageName, UserHandle user) { try { - mPM.grantPermission(packageName, permissionName, user.getIdentifier()); + return mPM.getPermissionFlags(permissionName, packageName, user.getIdentifier()); } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } } @Override - public void revokePermission(String packageName, String permissionName, UserHandle user) { + public void updatePermissionFlags(String permissionName, String packageName, + int flagMask, int flagValues, UserHandle user) { try { - mPM.revokePermission(packageName, permissionName, user.getIdentifier()); + mPM.updatePermissionFlags(permissionName, packageName, flagMask, + flagValues, user.getIdentifier()); } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java index 49644a7..1fb88a9 100644 --- a/core/java/android/app/BackStackRecord.java +++ b/core/java/android/app/BackStackRecord.java @@ -577,46 +577,6 @@ final class BackStackRecord extends FragmentTransaction implements return this; } - /** TODO: remove this */ - @Override - public FragmentTransaction setSharedElement(View sharedElement, String name) { - String transitionName = sharedElement.getTransitionName(); - if (transitionName == null) { - throw new IllegalArgumentException("Unique transitionNames are required for all" + - " sharedElements"); - } - mSharedElementSourceNames = new ArrayList<String>(1); - mSharedElementSourceNames.add(transitionName); - - mSharedElementTargetNames = new ArrayList<String>(1); - mSharedElementTargetNames.add(name); - return this; - } - - /** TODO: remove this */ - @Override - public FragmentTransaction setSharedElements(Pair<View, String>... sharedElements) { - if (sharedElements == null || sharedElements.length == 0) { - mSharedElementSourceNames = null; - mSharedElementTargetNames = null; - } else { - ArrayList<String> sourceNames = new ArrayList<String>(sharedElements.length); - ArrayList<String> targetNames = new ArrayList<String>(sharedElements.length); - for (int i = 0; i < sharedElements.length; i++) { - String transitionName = sharedElements[i].first.getTransitionName(); - if (transitionName == null) { - throw new IllegalArgumentException("Unique transitionNames are required for all" - + " sharedElements"); - } - sourceNames.add(transitionName); - targetNames.add(sharedElements[i].second); - } - mSharedElementSourceNames = sourceNames; - mSharedElementTargetNames = targetNames; - } - return this; - } - public FragmentTransaction setTransitionStyle(int styleRes) { mTransitionStyle = styleRes; return this; diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index 4db4be0..05cf1d4 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -593,7 +593,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { } private boolean allowOverlappingTransitions() { - return mIsReturning ? getWindow().getAllowExitTransitionOverlap() + return mIsReturning ? getWindow().getAllowReturnTransitionOverlap() : getWindow().getAllowEnterTransitionOverlap(); } diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 6b5239d..e0a30ad 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -945,7 +945,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate if (f.mView != null) { // Need to save the current view state if not // done already. - if (!mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) { + if (mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) { saveFragmentViewState(f); } } diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java index dc7075c..876c0f6 100644 --- a/core/java/android/app/FragmentTransaction.java +++ b/core/java/android/app/FragmentTransaction.java @@ -172,14 +172,6 @@ public abstract class FragmentTransaction { public abstract FragmentTransaction setTransition(int transit); /** - * TODO: remove from API - * @hide - */ - public FragmentTransaction setCustomTransition(int sceneRootId, int transitionId) { - return this; - } - - /** * Used with to map a View from a removed or hidden Fragment to a View from a shown * or added Fragment. * @param sharedElement A View in a disappearing Fragment to match with a View in an @@ -190,18 +182,6 @@ public abstract class FragmentTransaction { public abstract FragmentTransaction addSharedElement(View sharedElement, String name); /** - * TODO: remove from API - * @hide - */ - public abstract FragmentTransaction setSharedElement(View sharedElement, String name); - - /** - * TODO: remove from API - * @hide - */ - public abstract FragmentTransaction setSharedElements(Pair<View, String>... sharedElements); - - /** * Set a custom style resource that will be used for resolving transit * animations. */ diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 5829fbe..05a936c 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -403,6 +403,9 @@ public interface IActivityManager extends IInterface { public void keyguardWaitingForActivityDrawn() throws RemoteException; + public void keyguardGoingAway(boolean disableWindowAnimations, + boolean keyguardGoingToNotificationShade) throws RemoteException; + public boolean shouldUpRecreateTask(IBinder token, String destAffinity) throws RemoteException; @@ -842,4 +845,5 @@ public interface IActivityManager extends IInterface { int SHOW_LOCK_TASK_ESCAPE_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+294; int UPDATE_DEVICE_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+295; int UPDATE_PREFERRED_SETUP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+296; + int KEYGUARD_GOING_AWAY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+297; } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 49b2549..3309443 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -30,6 +30,7 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.session.MediaSession; @@ -885,6 +886,9 @@ public class Notification implements Parcelable */ public static final int HEADS_UP_REQUESTED = 2; + private Icon mSmallIcon; + private Icon mLargeIcon; + /** * Structure to encapsulate a named action that can be shown as part of this notification. * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is @@ -1362,7 +1366,7 @@ public class Notification implements Parcelable int version = parcel.readInt(); when = parcel.readLong(); - icon = parcel.readInt(); + mSmallIcon = Icon.CREATOR.createFromParcel(parcel); number = parcel.readInt(); if (parcel.readInt() != 0) { contentIntent = PendingIntent.CREATOR.createFromParcel(parcel); @@ -1380,7 +1384,7 @@ public class Notification implements Parcelable contentView = RemoteViews.CREATOR.createFromParcel(parcel); } if (parcel.readInt() != 0) { - largeIcon = Bitmap.CREATOR.createFromParcel(parcel); + mLargeIcon = Icon.CREATOR.createFromParcel(parcel); } defaults = parcel.readInt(); flags = parcel.readInt(); @@ -1445,7 +1449,7 @@ public class Notification implements Parcelable */ public void cloneInto(Notification that, boolean heavy) { that.when = this.when; - that.icon = this.icon; + that.mSmallIcon = this.mSmallIcon; that.number = this.number; // PendingIntents are global, so there's no reason (or way) to clone them. @@ -1462,8 +1466,8 @@ public class Notification implements Parcelable if (heavy && this.contentView != null) { that.contentView = this.contentView.clone(); } - if (heavy && this.largeIcon != null) { - that.largeIcon = Bitmap.createBitmap(this.largeIcon); + if (heavy && this.mLargeIcon != null) { + that.mLargeIcon = this.mLargeIcon; } that.iconLevel = this.iconLevel; that.sound = this.sound; // android.net.Uri is immutable @@ -1544,7 +1548,7 @@ public class Notification implements Parcelable contentView = null; bigContentView = null; headsUpContentView = null; - largeIcon = null; + mLargeIcon = null; if (extras != null) { extras.remove(Notification.EXTRA_LARGE_ICON); extras.remove(Notification.EXTRA_LARGE_ICON_BIG); @@ -1586,7 +1590,7 @@ public class Notification implements Parcelable parcel.writeInt(1); parcel.writeLong(when); - parcel.writeInt(icon); + mSmallIcon.writeToParcel(parcel, 0); parcel.writeInt(number); if (contentIntent != null) { parcel.writeInt(1); @@ -1618,9 +1622,9 @@ public class Notification implements Parcelable } else { parcel.writeInt(0); } - if (largeIcon != null) { + if (mLargeIcon != null) { parcel.writeInt(1); - largeIcon.writeToParcel(parcel, 0); + mLargeIcon.writeToParcel(parcel, 0); } else { parcel.writeInt(0); } @@ -1865,6 +1869,35 @@ public class Notification implements Parcelable } /** + * The small icon representing this notification in the status bar and content view. + * + * @return the small icon representing this notification. + * + * @see Builder#getSmallIcon() + * @see Builder#setSmallIcon(Icon) + */ + public Icon getSmallIcon() { + return mSmallIcon; + } + + /** + * Used when notifying to clean up legacy small icons. + * @hide + */ + public void setSmallIcon(Icon icon) { + mSmallIcon = icon; + } + + /** + * The large icon shown in this notification's content view. + * @see Builder#getLargeIcon() + * @see Builder#setLargeIcon(Icon) + */ + public Icon getLargeIcon() { + return mLargeIcon; + } + + /** * @hide */ public boolean isValid() { @@ -1966,7 +1999,7 @@ public class Notification implements Parcelable private Context mContext; private long mWhen; - private int mSmallIcon; + private Icon mSmallIcon, mLargeIcon; private int mSmallIconLevel; private int mNumber; private CharSequence mContentTitle; @@ -1979,7 +2012,6 @@ public class Notification implements Parcelable private PendingIntent mFullScreenIntent; private CharSequence mTickerText; private RemoteViews mTickerView; - private Bitmap mLargeIcon; private Uri mSound; private int mAudioStreamType; private AudioAttributes mAudioAttributes; @@ -2160,8 +2192,9 @@ public class Notification implements Parcelable * @see Notification#icon */ public Builder setSmallIcon(@DrawableRes int icon) { - mSmallIcon = icon; - return this; + return setSmallIcon(icon != 0 + ? Icon.createWithResource(mContext, icon) + : null); } /** @@ -2176,8 +2209,20 @@ public class Notification implements Parcelable * @see Notification#iconLevel */ public Builder setSmallIcon(@DrawableRes int icon, int level) { - mSmallIcon = icon; mSmallIconLevel = level; + return setSmallIcon(icon); + } + + /** + * Set the small icon, which will be used to represent the notification in the + * status bar and content view (unless overriden there by a + * {@link #setLargeIcon(Bitmap) large icon}). + * + * @param icon An Icon object to use. + * @see Notification#icon + */ + public Builder setSmallIcon(Icon icon) { + mSmallIcon = icon; return this; } @@ -2324,14 +2369,24 @@ public class Notification implements Parcelable } /** - * Add a large icon to the notification (and the ticker on some devices). + * Add a large icon to the notification content view. * * In the platform template, this image will be shown on the left of the notification view - * in place of the {@link #setSmallIcon(int) small icon} (which will move to the right side). + * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small + * badge atop the large icon). + */ + public Builder setLargeIcon(Bitmap b) { + return setLargeIcon(b != null ? Icon.createWithBitmap(b) : null); + } + + /** + * Add a large icon to the notification content view. * - * @see Notification#largeIcon + * In the platform template, this image will be shown on the left of the notification view + * in place of the {@link #setSmallIcon(Icon) small icon} (which will be placed in a small + * badge atop the large icon). */ - public Builder setLargeIcon(Bitmap icon) { + public Builder setLargeIcon(Icon icon) { mLargeIcon = icon; return this; } @@ -2840,13 +2895,13 @@ public class Notification implements Parcelable boolean contentTextInLine2 = false; if (mLargeIcon != null) { - contentView.setImageViewBitmap(R.id.icon, mLargeIcon); + contentView.setImageViewIcon(R.id.icon, mLargeIcon); processLargeLegacyIcon(mLargeIcon, contentView); - contentView.setImageViewResource(R.id.right_icon, mSmallIcon); + contentView.setImageViewIcon(R.id.right_icon, mSmallIcon); contentView.setViewVisibility(R.id.right_icon, View.VISIBLE); processSmallRightIcon(mSmallIcon, contentView); } else { // small icon at left - contentView.setImageViewResource(R.id.icon, mSmallIcon); + contentView.setImageViewIcon(R.id.icon, mSmallIcon); contentView.setViewVisibility(R.id.icon, View.VISIBLE); processSmallIconAsLarge(mSmallIcon, contentView); } @@ -3086,14 +3141,16 @@ public class Notification implements Parcelable /** * Apply any necessary background to smallIcons being used in the largeIcon spot. */ - private void processSmallIconAsLarge(int largeIconId, RemoteViews contentView) { + private void processSmallIconAsLarge(Icon largeIcon, RemoteViews contentView) { if (!isLegacy()) { contentView.setDrawableParameters(R.id.icon, false, -1, 0xFFFFFFFF, PorterDuff.Mode.SRC_ATOP, -1); - } - if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, largeIconId)) { applyLargeIconBackground(contentView); + } else { + if (mColorUtil.isGrayscaleIcon(mContext, largeIcon)) { + applyLargeIconBackground(contentView); + } } } @@ -3102,8 +3159,9 @@ public class Notification implements Parcelable * if it's grayscale). */ // TODO: also check bounds, transparency, that sort of thing. - private void processLargeLegacyIcon(Bitmap largeIcon, RemoteViews contentView) { - if (isLegacy() && mColorUtil.isGrayscaleIcon(largeIcon)) { + private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) { + if (largeIcon != null && isLegacy() + && mColorUtil.isGrayscaleIcon(mContext, largeIcon)) { applyLargeIconBackground(contentView); } else { removeLargeIconBackground(contentView); @@ -3137,14 +3195,16 @@ public class Notification implements Parcelable /** * Recolor small icons when used in the R.id.right_icon slot. */ - private void processSmallRightIcon(int smallIconDrawableId, - RemoteViews contentView) { + private void processSmallRightIcon(Icon smallIcon, RemoteViews contentView) { if (!isLegacy()) { contentView.setDrawableParameters(R.id.right_icon, false, -1, 0xFFFFFFFF, PorterDuff.Mode.SRC_ATOP, -1); } - if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, smallIconDrawableId)) { + final boolean gray = isLegacy() + && smallIcon.getType() == Icon.TYPE_RESOURCE + && mColorUtil.isGrayscaleIcon(mContext, smallIcon.getResId()); + if (!isLegacy() || gray) { contentView.setInt(R.id.right_icon, "setBackgroundResource", R.drawable.notification_icon_legacy_bg); @@ -3180,7 +3240,10 @@ public class Notification implements Parcelable public Notification buildUnstyled() { Notification n = new Notification(); n.when = mWhen; - n.icon = mSmallIcon; + n.mSmallIcon = mSmallIcon; + if (mSmallIcon.getType() == Icon.TYPE_RESOURCE) { + n.icon = mSmallIcon.getResId(); + } n.iconLevel = mSmallIconLevel; n.number = mNumber; @@ -3192,7 +3255,10 @@ public class Notification implements Parcelable n.fullScreenIntent = mFullScreenIntent; n.tickerText = mTickerText; n.tickerView = makeTickerView(); - n.largeIcon = mLargeIcon; + n.mLargeIcon = mLargeIcon; + if (mLargeIcon != null && mLargeIcon.getType() == Icon.TYPE_BITMAP) { + n.largeIcon = mLargeIcon.getBitmap(); + } n.sound = mSound; n.audioStreamType = mAudioStreamType; n.audioAttributes = mAudioAttributes; @@ -3242,7 +3308,7 @@ public class Notification implements Parcelable extras.putCharSequence(EXTRA_TEXT, mContentText); extras.putCharSequence(EXTRA_SUB_TEXT, mSubText); extras.putCharSequence(EXTRA_INFO_TEXT, mContentInfo); - extras.putInt(EXTRA_SMALL_ICON, mSmallIcon); + extras.putParcelable(EXTRA_SMALL_ICON, mSmallIcon); extras.putInt(EXTRA_PROGRESS, mProgress); extras.putInt(EXTRA_PROGRESS_MAX, mProgressMax); extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, mProgressIndeterminate); @@ -3430,7 +3496,7 @@ public class Notification implements Parcelable // Notification fields. mWhen = n.when; - mSmallIcon = n.icon; + mSmallIcon = n.mSmallIcon; mSmallIconLevel = n.iconLevel; mNumber = n.number; @@ -3441,7 +3507,7 @@ public class Notification implements Parcelable mFullScreenIntent = n.fullScreenIntent; mTickerText = n.tickerText; mTickerView = n.tickerView; - mLargeIcon = n.largeIcon; + mLargeIcon = n.mLargeIcon; mSound = n.sound; mAudioStreamType = n.audioStreamType; mAudioAttributes = n.audioAttributes; @@ -3472,7 +3538,7 @@ public class Notification implements Parcelable mContentText = extras.getCharSequence(EXTRA_TEXT); mSubText = extras.getCharSequence(EXTRA_SUB_TEXT); mContentInfo = extras.getCharSequence(EXTRA_INFO_TEXT); - mSmallIcon = extras.getInt(EXTRA_SMALL_ICON); + mSmallIcon = extras.getParcelable(EXTRA_SMALL_ICON); mProgress = extras.getInt(EXTRA_PROGRESS); mProgressMax = extras.getInt(EXTRA_PROGRESS_MAX); mProgressIndeterminate = extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE); @@ -3764,7 +3830,7 @@ public class Notification implements Parcelable */ public static class BigPictureStyle extends Style { private Bitmap mPicture; - private Bitmap mBigLargeIcon; + private Icon mBigLargeIcon; private boolean mBigLargeIconSet = false; public BigPictureStyle() { @@ -3803,8 +3869,15 @@ public class Notification implements Parcelable * Override the large icon when the big notification is shown. */ public BigPictureStyle bigLargeIcon(Bitmap b) { + return bigLargeIcon(b != null ? Icon.createWithBitmap(b) : null); + } + + /** + * Override the large icon when the big notification is shown. + */ + public BigPictureStyle bigLargeIcon(Icon icon) { mBigLargeIconSet = true; - mBigLargeIcon = b; + mBigLargeIcon = icon; return this; } @@ -3815,7 +3888,7 @@ public class Notification implements Parcelable // 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides // mLargeIcon // 2. !mBigLargeIconSet -> mLargeIcon applies - Bitmap oldLargeIcon = null; + Icon oldLargeIcon = null; if (mBigLargeIconSet) { oldLargeIcon = mBuilder.mLargeIcon; mBuilder.mLargeIcon = mBigLargeIcon; diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index e4bbe27..557964b 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -23,6 +23,7 @@ import android.app.Notification.Builder; import android.content.ComponentName; import android.content.Context; import android.content.pm.ParceledListSlice; +import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -204,6 +205,7 @@ public class NotificationManager notification.sound.checkFileUriExposed("Notification.sound"); } } + fixLegacySmallIcon(notification, pkg); if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); Notification stripped = notification.clone(); Builder.stripForDelivery(stripped); @@ -231,6 +233,7 @@ public class NotificationManager notification.sound.checkFileUriExposed("Notification.sound"); } } + fixLegacySmallIcon(notification, pkg); if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); Notification stripped = notification.clone(); Builder.stripForDelivery(stripped); @@ -244,6 +247,12 @@ public class NotificationManager } } + private void fixLegacySmallIcon(Notification n, String pkg) { + if (n.getSmallIcon() == null && n.icon != 0) { + n.setSmallIcon(Icon.createWithResource(pkg, n.icon)); + } + } + /** * Cancel a previously shown notification. If it's transient, the view * will be hidden. If it's persistent, it will be removed from the status diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index 470804d..87e2f9a 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -24,6 +24,7 @@ import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import android.security.KeyChain; @@ -249,13 +250,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver { public static final String EXTRA_CHOOSE_PRIVATE_KEY_SENDER_UID = "android.app.extra.CHOOSE_PRIVATE_KEY_SENDER_UID"; /** @hide */ - public static final String EXTRA_CHOOSE_PRIVATE_KEY_HOST = "android.app.extra.CHOOSE_PRIVATE_KEY_HOST"; - - /** @hide */ - public static final String EXTRA_CHOOSE_PRIVATE_KEY_PORT = "android.app.extra.CHOOSE_PRIVATE_KEY_PORT"; - - /** @hide */ - public static final String EXTRA_CHOOSE_PRIVATE_KEY_URL = "android.app.extra.CHOOSE_PRIVATE_KEY_URL"; + public static final String EXTRA_CHOOSE_PRIVATE_KEY_URI = "android.app.extra.CHOOSE_PRIVATE_KEY_URI"; /** @hide */ public static final String EXTRA_CHOOSE_PRIVATE_KEY_ALIAS = "android.app.extra.CHOOSE_PRIVATE_KEY_ALIAS"; @@ -487,15 +482,13 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * @param context The running context as per {@link #onReceive}. * @param intent The received intent as per {@link #onReceive}. * @param uid The uid asking for the private key and certificate pair. - * @param host The authentication host, may be null. - * @param port The authentication port, or -1. - * @param url The URL to authenticate, may be null. + * @param uri The URI to authenticate, may be null. * @param alias The alias preselected by the client, or null. * @return The private key alias to return and grant access to. * @see KeyChain#choosePrivateKeyAlias */ - public String onChoosePrivateKeyAlias(Context context, Intent intent, int uid, String host, - int port, String url, String alias) { + public String onChoosePrivateKeyAlias(Context context, Intent intent, int uid, Uri uri, + String alias) { return null; } @@ -546,12 +539,9 @@ public class DeviceAdminReceiver extends BroadcastReceiver { onProfileProvisioningComplete(context, intent); } else if (ACTION_CHOOSE_PRIVATE_KEY_ALIAS.equals(action)) { int uid = intent.getIntExtra(EXTRA_CHOOSE_PRIVATE_KEY_SENDER_UID, -1); - String host = intent.getStringExtra(EXTRA_CHOOSE_PRIVATE_KEY_HOST); - int port = intent.getIntExtra(EXTRA_CHOOSE_PRIVATE_KEY_PORT, -1); - String url = intent.getStringExtra(EXTRA_CHOOSE_PRIVATE_KEY_URL); + Uri uri = intent.getParcelableExtra(EXTRA_CHOOSE_PRIVATE_KEY_URI); String alias = intent.getStringExtra(EXTRA_CHOOSE_PRIVATE_KEY_ALIAS); - String chosenAlias = onChoosePrivateKeyAlias(context, intent, uid, host, port, url, - alias); + String chosenAlias = onChoosePrivateKeyAlias(context, intent, uid, uri, alias); setResultData(chosenAlias); } else if (ACTION_LOCK_TASK_ENTERING.equals(action)) { String pkg = intent.getStringExtra(EXTRA_LOCK_TASK_PACKAGE); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index ae07206..55ff85a 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -402,7 +402,7 @@ public class DevicePolicyManager { * has completed successfully. * * <p>The broadcast is limited to the primary profile, to the app specified in the provisioning - * intent (@see #ACTION_PROVISION_MANAGED_PROFILE). + * intent with action {@link #ACTION_PROVISION_MANAGED_PROFILE}. * * <p>This intent will contain the extra {@link #EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE} which * corresponds to the account requested to be migrated at provisioning time, if any. @@ -2523,13 +2523,26 @@ public class DevicePolicyManager { * {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_FEATURES} to be able to call * this method; if it has not, a security exception will be thrown. * - * <p>Calling this from a managed profile will throw a security exception. + * <p>Calling this from a managed profile before version + * {@link android.os.Build.VERSION_CODES#MNC} will throw a security exception. + * + * <p>From version {@link android.os.Build.VERSION_CODES#MNC} a profile owner can set: + * <ul> + * <li>{@link #KEYGUARD_DISABLE_TRUST_AGENTS}, {@link #KEYGUARD_DISABLE_FINGERPRINT} + * these will affect the profile's parent user. + * <li>{@link #KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS} this will affect notifications + * generated by applications in the managed profile. + * </ul> + * <p>Requests to disable other features on a managed profile will be ignored. The admin + * can check which features have been disabled by calling + * {@link #getKeyguardDisabledFeatures(ComponentName)} * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param which {@link #KEYGUARD_DISABLE_FEATURES_NONE} (default), * {@link #KEYGUARD_DISABLE_WIDGETS_ALL}, {@link #KEYGUARD_DISABLE_SECURE_CAMERA}, * {@link #KEYGUARD_DISABLE_SECURE_NOTIFICATIONS}, {@link #KEYGUARD_DISABLE_TRUST_AGENTS}, - * {@link #KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS}, {@link #KEYGUARD_DISABLE_FEATURES_ALL} + * {@link #KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS}, {@link #KEYGUARD_DISABLE_FINGERPRINT}, + * {@link #KEYGUARD_DISABLE_FEATURES_ALL} */ public void setKeyguardDisabledFeatures(ComponentName admin, int which) { if (mService != null) { @@ -2793,17 +2806,16 @@ public class DevicePolicyManager { * @param who Which {@link DeviceAdminReceiver} this request is associated with, or null if not * called by the device owner. * @param initializer Which {@link DeviceAdminReceiver} to make device initializer. - * @param initializerName The user-visible name of the device initializer. * @return whether the component was successfully registered as the device initializer. * @throws IllegalArgumentException if the componentname is null or invalid * @throws IllegalStateException if the caller is not device owner or the device has * already been provisioned or a device initializer already exists. */ - public boolean setDeviceInitializer(ComponentName who, ComponentName initializer, - String initializerName) throws IllegalArgumentException, IllegalStateException { + public boolean setDeviceInitializer(ComponentName who, ComponentName initializer) + throws IllegalArgumentException, IllegalStateException { if (mService != null) { try { - return mService.setDeviceInitializer(who, initializer, initializerName); + return mService.setDeviceInitializer(who, initializer); } catch (RemoteException re) { Log.w(TAG, "Failed to set device initializer"); } @@ -4253,11 +4265,7 @@ public class DevicePolicyManager { public void setSystemUpdatePolicy(ComponentName who, SystemUpdatePolicy policy) { if (mService != null) { try { - if (policy != null) { - mService.setSystemUpdatePolicy(who, policy.getPolicyBundle()); - } else { - mService.setSystemUpdatePolicy(who, null); - } + mService.setSystemUpdatePolicy(who, policy); } catch (RemoteException re) { Log.w(TAG, "Error calling setSystemUpdatePolicy", re); } @@ -4272,12 +4280,7 @@ public class DevicePolicyManager { public SystemUpdatePolicy getSystemUpdatePolicy() { if (mService != null) { try { - PersistableBundle bundle = mService.getSystemUpdatePolicy(); - if (bundle != null) { - return new SystemUpdatePolicy(bundle); - } else { - return null; - } + return mService.getSystemUpdatePolicy(); } catch (RemoteException re) { Log.w(TAG, "Error calling getSystemUpdatePolicy", re); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index e81e7c1..24ef604 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -17,11 +17,13 @@ package android.app.admin; +import android.app.admin.SystemUpdatePolicy; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Bitmap; import android.net.ProxyInfo; +import android.net.Uri; import android.os.Bundle; import android.os.PersistableBundle; import android.os.RemoteCallback; @@ -130,7 +132,7 @@ interface IDevicePolicyManager { void enforceCanManageCaCerts(in ComponentName admin); boolean installKeyPair(in ComponentName who, in byte[] privKeyBuffer, in byte[] certBuffer, String alias); - void choosePrivateKeyAlias(int uid, in String host, int port, in String url, in String alias, IBinder aliasCallback); + void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback); void setCertInstallerPackage(in ComponentName who, String installerPackage); String getCertInstallerPackage(in ComponentName who); @@ -212,7 +214,7 @@ interface IDevicePolicyManager { boolean setUserEnabled(in ComponentName who); boolean isDeviceInitializer(String packageName); void clearDeviceInitializer(in ComponentName who); - boolean setDeviceInitializer(in ComponentName who, in ComponentName initializer, String initializerName); + boolean setDeviceInitializer(in ComponentName who, in ComponentName initializer); String getDeviceInitializer(); ComponentName getDeviceInitializerComponent(); @@ -221,8 +223,8 @@ interface IDevicePolicyManager { void setUserIcon(in ComponentName admin, in Bitmap icon); void sendDeviceInitializerStatus(int statusCode, String description); - void setSystemUpdatePolicy(in ComponentName who, in PersistableBundle policy); - PersistableBundle getSystemUpdatePolicy(); + void setSystemUpdatePolicy(in ComponentName who, in SystemUpdatePolicy policy); + SystemUpdatePolicy getSystemUpdatePolicy(); boolean setKeyguardDisabled(in ComponentName admin, boolean disabled); boolean setStatusBarDisabled(in ComponentName who, boolean disabled); diff --git a/core/java/android/app/admin/SystemUpdatePolicy.aidl b/core/java/android/app/admin/SystemUpdatePolicy.aidl new file mode 100644 index 0000000..58e8d15 --- /dev/null +++ b/core/java/android/app/admin/SystemUpdatePolicy.aidl @@ -0,0 +1,20 @@ +/* +** +** Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.app.admin; + +parcelable SystemUpdatePolicy; diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java index de56cd0..20ddb77 100644 --- a/core/java/android/app/admin/SystemUpdatePolicy.java +++ b/core/java/android/app/admin/SystemUpdatePolicy.java @@ -17,8 +17,15 @@ package android.app.admin; import android.annotation.IntDef; +import android.os.Parcel; +import android.os.Parcelable; import android.os.PersistableBundle; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -28,7 +35,7 @@ import java.lang.annotation.RetentionPolicy; * @see DevicePolicyManager#setSystemUpdatePolicy * @see DevicePolicyManager#getSystemUpdatePolicy */ -public class SystemUpdatePolicy { +public class SystemUpdatePolicy implements Parcelable { /** @hide */ @IntDef({ @@ -39,6 +46,10 @@ public class SystemUpdatePolicy { @interface SystemUpdatePolicyType {} /** + * Unknown policy type, used only internally. + */ + private static final int TYPE_UNKNOWN = -1; + /** * Install system update automatically as soon as one is available. */ public static final int TYPE_INSTALL_AUTOMATIC = 1; @@ -63,45 +74,40 @@ public class SystemUpdatePolicy { private static final String KEY_POLICY_TYPE = "policy_type"; private static final String KEY_INSTALL_WINDOW_START = "install_window_start"; private static final String KEY_INSTALL_WINDOW_END = "install_window_end"; + /** + * The upper boundary of the daily maintenance window: 24 * 60 minutes. + */ + private static final int WINDOW_BOUNDARY = 24 * 60; - private PersistableBundle mPolicy; + @SystemUpdatePolicyType + private int mPolicyType; - public SystemUpdatePolicy() { - mPolicy = new PersistableBundle(); - } + private int mMaintenanceWindowStart; + private int mMaintenanceWindowEnd; - /** - * Construct an SystemUpdatePolicy object from a bundle. - * @hide - */ - public SystemUpdatePolicy(PersistableBundle in) { - mPolicy = new PersistableBundle(in); - } - /** - * Retrieve the underlying bundle where the policy is stored. - * @hide - */ - public PersistableBundle getPolicyBundle() { - return new PersistableBundle(mPolicy); + private SystemUpdatePolicy() { + mPolicyType = TYPE_UNKNOWN; } /** - * Set the policy to: install update automatically as soon as one is available. + * Create a policy object and set it to install update automatically as soon as one is + * available. * * @see #TYPE_INSTALL_AUTOMATIC */ - public void setAutomaticInstallPolicy() { - mPolicy.clear(); - mPolicy.putInt(KEY_POLICY_TYPE, TYPE_INSTALL_AUTOMATIC); + public static SystemUpdatePolicy createAutomaticInstallPolicy() { + SystemUpdatePolicy policy = new SystemUpdatePolicy(); + policy.mPolicyType = TYPE_INSTALL_AUTOMATIC; + return policy; } /** - * Set the policy to: new system update will only be installed automatically when the system - * clock is inside a daily maintenance window. If the start and end times are the same, the - * window is considered to include the WHOLE 24 hours, that is, updates can install at any time. - * If the given window in invalid, a {@link SystemUpdatePolicy.InvalidWindowException} will be - * thrown. If start time is later than end time, the window is considered spanning midnight, + * Create a policy object and set it to: new system update will only be installed automatically + * when the system clock is inside a daily maintenance window. If the start and end times are + * the same, the window is considered to include the WHOLE 24 hours, that is, updates can + * install at any time. If the given window in invalid, a {@link IllegalArgumentException} will + * be thrown. If start time is later than end time, the window is considered spanning midnight, * i.e. end time donates a time on the next day. The maintenance window will last for 30 days, * after which the system should revert back to its normal behavior as if no policy were set. * @@ -111,25 +117,29 @@ public class SystemUpdatePolicy { * midnight in the device's local time. Must be in the range of [0, 1440). * @see #TYPE_INSTALL_WINDOWED */ - public void setWindowedInstallPolicy(int startTime, int endTime) throws InvalidWindowException{ - if (startTime < 0 || startTime >= 1440 || endTime < 0 || endTime >= 1440) { - throw new InvalidWindowException("startTime and endTime must be inside [0, 1440)"); + public static SystemUpdatePolicy createWindowedInstallPolicy(int startTime, int endTime) { + if (startTime < 0 || startTime >= WINDOW_BOUNDARY + || endTime < 0 || endTime >= WINDOW_BOUNDARY) { + throw new IllegalArgumentException("startTime and endTime must be inside [0, 1440)"); } - mPolicy.clear(); - mPolicy.putInt(KEY_POLICY_TYPE, TYPE_INSTALL_WINDOWED); - mPolicy.putInt(KEY_INSTALL_WINDOW_START, startTime); - mPolicy.putInt(KEY_INSTALL_WINDOW_END, endTime); + SystemUpdatePolicy policy = new SystemUpdatePolicy(); + policy.mPolicyType = TYPE_INSTALL_WINDOWED; + policy.mMaintenanceWindowStart = startTime; + policy.mMaintenanceWindowEnd = endTime; + return policy; } /** - * Set the policy to: block installation for a maximum period of 30 days. After expiration the - * system should revert back to its normal behavior as if no policy were set. + * Create a policy object and set it to block installation for a maximum period of 30 days. + * After expiration the system should revert back to its normal behavior as if no policy were + * set. * * @see #TYPE_POSTPONE */ - public void setPostponeInstallPolicy() { - mPolicy.clear(); - mPolicy.putInt(KEY_POLICY_TYPE, TYPE_POSTPONE); + public static SystemUpdatePolicy createPostponeInstallPolicy() { + SystemUpdatePolicy policy = new SystemUpdatePolicy(); + policy.mPolicyType = TYPE_POSTPONE; + return policy; } /** @@ -140,7 +150,7 @@ public class SystemUpdatePolicy { */ @SystemUpdatePolicyType public int getPolicyType() { - return mPolicy.getInt(KEY_POLICY_TYPE, -1); + return mPolicyType; } /** @@ -150,8 +160,8 @@ public class SystemUpdatePolicy { * or -1 if the policy does not have a maintenance window. */ public int getInstallWindowStart() { - if (getPolicyType() == TYPE_INSTALL_WINDOWED) { - return mPolicy.getInt(KEY_INSTALL_WINDOW_START, -1); + if (mPolicyType == TYPE_INSTALL_WINDOWED) { + return mMaintenanceWindowStart; } else { return -1; } @@ -164,26 +174,98 @@ public class SystemUpdatePolicy { * or -1 if the policy does not have a maintenance window. */ public int getInstallWindowEnd() { - if (getPolicyType() == TYPE_INSTALL_WINDOWED) { - return mPolicy.getInt(KEY_INSTALL_WINDOW_END, -1); + if (mPolicyType == TYPE_INSTALL_WINDOWED) { + return mMaintenanceWindowEnd; } else { return -1; } } + /** + * Return if this object represents a valid policy. + * @hide + */ + public boolean isValid() { + if (mPolicyType == TYPE_INSTALL_AUTOMATIC || mPolicyType == TYPE_POSTPONE) { + return true; + } else if (mPolicyType == TYPE_INSTALL_WINDOWED) { + return mMaintenanceWindowStart >= 0 && mMaintenanceWindowStart < WINDOW_BOUNDARY + && mMaintenanceWindowEnd >= 0 && mMaintenanceWindowEnd < WINDOW_BOUNDARY; + } else { + return false; + } + } + @Override public String toString() { - return mPolicy.toString(); + return String.format("SystemUpdatePolicy (type: %d, windowStart: %d, windowEnd: %d)", + mPolicyType, mMaintenanceWindowStart, mMaintenanceWindowEnd); + } + + @Override + public int describeContents() { + return 0; } + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mPolicyType); + dest.writeInt(mMaintenanceWindowStart); + dest.writeInt(mMaintenanceWindowEnd); + } + + public static final Parcelable.Creator<SystemUpdatePolicy> CREATOR = + new Parcelable.Creator<SystemUpdatePolicy>() { + + @Override + public SystemUpdatePolicy createFromParcel(Parcel source) { + SystemUpdatePolicy policy = new SystemUpdatePolicy(); + policy.mPolicyType = source.readInt(); + policy.mMaintenanceWindowStart = source.readInt(); + policy.mMaintenanceWindowEnd = source.readInt(); + return policy; + } + + @Override + public SystemUpdatePolicy[] newArray(int size) { + return new SystemUpdatePolicy[size]; + } + }; + + /** - * Exception thrown by {@link SystemUpdatePolicy#setWindowedInstallPolicy(int, int)} in case the - * specified window is invalid. + * @hide */ - public static class InvalidWindowException extends Exception { - public InvalidWindowException(String reason) { - super(reason); + public static SystemUpdatePolicy restoreFromXml(XmlPullParser parser) { + try { + SystemUpdatePolicy policy = new SystemUpdatePolicy(); + String value = parser.getAttributeValue(null, KEY_POLICY_TYPE); + if (value != null) { + policy.mPolicyType = Integer.parseInt(value); + + value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_START); + if (value != null) { + policy.mMaintenanceWindowStart = Integer.parseInt(value); + } + value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_END); + if (value != null) { + policy.mMaintenanceWindowEnd = Integer.parseInt(value); + } + return policy; + } + } catch (NumberFormatException e) { + // Fail through } + return null; + } + + /** + * @hide + */ + public void saveToXml(XmlSerializer out) throws IOException { + out.attribute(null, KEY_POLICY_TYPE, Integer.toString(mPolicyType)); + out.attribute(null, KEY_INSTALL_WINDOW_START, Integer.toString(mMaintenanceWindowStart)); + out.attribute(null, KEY_INSTALL_WINDOW_END, Integer.toString(mMaintenanceWindowEnd)); } } diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java index 9540eb1..4d2158f 100644 --- a/core/java/android/app/backup/BackupTransport.java +++ b/core/java/android/app/backup/BackupTransport.java @@ -162,8 +162,17 @@ public class BackupTransport { * this is called, {@link #finishBackup} will be called to ensure the request * is sent and received successfully. * + * <p>If the transport returns anything other than TRANSPORT_OK from this method, + * the OS will halt the current initialize operation and schedule a retry in the + * near future. Even if the transport is in a state such that attempting to + * "initialize" the backend storage is meaningless -- for example, if there is + * no current live dataset at all, or there is no authenticated account under which + * to store the data remotely -- the transport should return TRANSPORT_OK here + * and treat the initializeDevice() / finishBackup() pair as a graceful no-op. + * * @return One of {@link BackupTransport#TRANSPORT_OK} (OK so far) or - * {@link BackupTransport#TRANSPORT_ERROR} (on network error or other failure). + * {@link BackupTransport#TRANSPORT_ERROR} (to retry following network error + * or other failure). */ public int initializeDevice() { return BackupTransport.TRANSPORT_ERROR; diff --git a/core/java/android/app/usage/NetworkUsageStats.java b/core/java/android/app/usage/NetworkStats.java index 990d231..5193563 100644 --- a/core/java/android/app/usage/NetworkUsageStats.java +++ b/core/java/android/app/usage/NetworkStats.java @@ -19,7 +19,6 @@ package android.app.usage; import android.content.Context; import android.net.INetworkStatsService; import android.net.INetworkStatsSession; -import android.net.NetworkStats; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.TrafficStats; @@ -33,7 +32,7 @@ import dalvik.system.CloseGuard; * Class providing enumeration over buckets of network usage statistics. NetworkUsageStats objects * are returned as results to various queries in {@link NetworkStatsManager}. */ -public final class NetworkUsageStats implements AutoCloseable { +public final class NetworkStats implements AutoCloseable { private final static String TAG = "NetworkUsageStats"; private final CloseGuard mCloseGuard = CloseGuard.get(); @@ -70,7 +69,7 @@ public final class NetworkUsageStats implements AutoCloseable { /** * Results of a summary query. */ - private NetworkStats mSummary = null; + private android.net.NetworkStats mSummary = null; /** * Results of detail queries. @@ -85,11 +84,11 @@ public final class NetworkUsageStats implements AutoCloseable { /** * Recycling entry objects to prevent heap fragmentation. */ - private NetworkStats.Entry mRecycledSummaryEntry = null; + private android.net.NetworkStats.Entry mRecycledSummaryEntry = null; private NetworkStatsHistory.Entry mRecycledHistoryEntry = null; /** @hide */ - NetworkUsageStats(Context context, NetworkTemplate template, long startTimestamp, + NetworkStats(Context context, NetworkTemplate template, long startTimestamp, long endTimestamp) throws RemoteException, SecurityException { final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface( ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); @@ -136,14 +135,19 @@ public final class NetworkUsageStats implements AutoCloseable { public static final int STATE_FOREGROUND = 0x2; /** + * Special UID value for aggregate/unspecified. + */ + public static final int UID_ALL = android.net.NetworkStats.UID_ALL; + + /** * Special UID value for removed apps. */ - public static final int UID_REMOVED = -4; + public static final int UID_REMOVED = TrafficStats.UID_REMOVED; /** * Special UID value for data usage by tethering. */ - public static final int UID_TETHERING = -5; + public static final int UID_TETHERING = TrafficStats.UID_TETHERING; private int mUid; private int mState; @@ -156,9 +160,9 @@ public final class NetworkUsageStats implements AutoCloseable { private static int convertState(int networkStatsSet) { switch (networkStatsSet) { - case NetworkStats.SET_ALL : return STATE_ALL; - case NetworkStats.SET_DEFAULT : return STATE_DEFAULT; - case NetworkStats.SET_FOREGROUND : return STATE_FOREGROUND; + case android.net.NetworkStats.SET_ALL : return STATE_ALL; + case android.net.NetworkStats.SET_DEFAULT : return STATE_DEFAULT; + case android.net.NetworkStats.SET_FOREGROUND : return STATE_FOREGROUND; } return 0; } @@ -337,8 +341,8 @@ public final class NetworkUsageStats implements AutoCloseable { void startHistoryEnumeration(int uid) { mHistory = null; try { - mHistory = mSession.getHistoryForUid(mTemplate, uid, NetworkStats.SET_ALL, - NetworkStats.TAG_NONE, NetworkStatsHistory.FIELD_ALL); + mHistory = mSession.getHistoryForUid(mTemplate, uid, android.net.NetworkStats.SET_ALL, + android.net.NetworkStats.TAG_NONE, NetworkStatsHistory.FIELD_ALL); setSingleUid(uid); } catch (RemoteException e) { Log.w(TAG, e); @@ -364,8 +368,9 @@ public final class NetworkUsageStats implements AutoCloseable { stepUid(); mHistory = null; try { - mHistory = mSession.getHistoryForUid(mTemplate, getUid(), NetworkStats.SET_ALL, - NetworkStats.TAG_NONE, NetworkStatsHistory.FIELD_ALL); + mHistory = mSession.getHistoryForUid(mTemplate, getUid(), + android.net.NetworkStats.SET_ALL, android.net.NetworkStats.TAG_NONE, + NetworkStatsHistory.FIELD_ALL); } catch (RemoteException e) { Log.w(TAG, e); // Leaving mHistory null @@ -405,7 +410,7 @@ public final class NetworkUsageStats implements AutoCloseable { } Bucket bucket = new Bucket(); if (mRecycledSummaryEntry == null) { - mRecycledSummaryEntry = new NetworkStats.Entry(); + mRecycledSummaryEntry = new android.net.NetworkStats.Entry(); } mSummary.getTotal(mRecycledSummaryEntry); fillBucketFromSummaryEntry(bucket); diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java index af7c053..2ae0181 100644 --- a/core/java/android/app/usage/NetworkStatsManager.java +++ b/core/java/android/app/usage/NetworkStatsManager.java @@ -16,18 +16,17 @@ package android.app.usage; -import android.app.usage.NetworkUsageStats.Bucket; +import android.app.usage.NetworkStats.Bucket; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkIdentity; import android.net.NetworkTemplate; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; /** * Provides access to network usage history and statistics. Usage data is collected in - * discrete bins of time called 'Buckets'. See {@link NetworkUsageStats.Bucket} for details. + * discrete bins of time called 'Buckets'. See {@link NetworkStats.Bucket} for details. * <p /> * Queries can define a time interval in the form of start and end timestamps (Long.MIN_VALUE and * Long.MAX_VALUE can be used to simulate open ended intervals). All queries (except @@ -37,15 +36,20 @@ import android.util.Log; * <h3> * Summary queries * </h3> + * {@link #querySummaryForDevice} <p /> + * {@link #querySummaryForUser} <p /> + * {@link #querySummary} <p /> * These queries aggregate network usage across the whole interval. Therefore there will be only one * bucket for a particular key and state combination. In case of the user-wide and device-wide * summaries a single bucket containing the totalised network usage is returned. * <h3> * History queries * </h3> + * {@link #queryDetailsForUid} <p /> + * {@link #queryDetails} <p /> * These queries do not aggregate over time but do aggregate over state. Therefore there can be * multiple buckets for a particular key but all Bucket's state is going to be - * {@link NetworkUsageStats.Bucket#STATE_ALL}. + * {@link NetworkStats.Bucket#STATE_ALL}. * <p /> * <b>NOTE:</b> This API requires the permission * {@link android.Manifest.permission#PACKAGE_USAGE_STATS}, which is a system-level permission and @@ -68,7 +72,10 @@ public class NetworkStatsManager { } /** * Query network usage statistics summaries. Result is summarised data usage for the whole - * device. Result is a single Bucket aggregated over time, state and uid. + * device. Result is a single Bucket aggregated over time, state and uid. This means the + * bucket's start and end timestamp are going to be the same as the 'startTime' and 'endTime' + * parameters, state is going to be {@link NetworkStats.Bucket#STATE_ALL} and uid + * {@link NetworkStats.Bucket#UID_ALL}. * * @param networkType As defined in {@link ConnectivityManager}, e.g. * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} @@ -89,7 +96,7 @@ public class NetworkStatsManager { } Bucket bucket = null; - NetworkUsageStats stats = new NetworkUsageStats(mContext, template, startTime, endTime); + NetworkStats stats = new NetworkStats(mContext, template, startTime, endTime); bucket = stats.getDeviceSummaryForNetwork(startTime, endTime); stats.close(); @@ -99,6 +106,9 @@ public class NetworkStatsManager { /** * Query network usage statistics summaries. Result is summarised data usage for all uids * belonging to calling user. Result is a single Bucket aggregated over time, state and uid. + * This means the bucket's start and end timestamp are going to be the same as the 'startTime' + * and 'endTime' parameters, state is going to be {@link NetworkStats.Bucket#STATE_ALL} and uid + * {@link NetworkStats.Bucket#UID_ALL}. * * @param networkType As defined in {@link ConnectivityManager}, e.g. * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} @@ -118,8 +128,8 @@ public class NetworkStatsManager { return null; } - NetworkUsageStats stats; - stats = new NetworkUsageStats(mContext, template, startTime, endTime); + NetworkStats stats; + stats = new NetworkStats(mContext, template, startTime, endTime); stats.startSummaryEnumeration(startTime, endTime); stats.close(); @@ -129,7 +139,9 @@ public class NetworkStatsManager { /** * Query network usage statistics summaries. Result filtered to include only uids belonging to * calling user. Result is aggregated over time, hence all buckets will have the same start and - * end timestamps. Not aggregated over state or uid. + * end timestamps. Not aggregated over state or uid. This means buckets' start and end + * timestamps are going to be the same as the 'startTime' and 'endTime' parameters, state and + * uid are going to vary. * * @param networkType As defined in {@link ConnectivityManager}, e.g. * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} @@ -142,15 +154,15 @@ public class NetworkStatsManager { * @return Statistics object or null if permissions are insufficient or error happened during * statistics collection. */ - public NetworkUsageStats querySummary(int networkType, String subscriberId, long startTime, + public NetworkStats querySummary(int networkType, String subscriberId, long startTime, long endTime) throws SecurityException, RemoteException { NetworkTemplate template = createTemplate(networkType, subscriberId); if (template == null) { return null; } - NetworkUsageStats result; - result = new NetworkUsageStats(mContext, template, startTime, endTime); + NetworkStats result; + result = new NetworkStats(mContext, template, startTime, endTime); result.startSummaryEnumeration(startTime, endTime); return result; @@ -158,7 +170,9 @@ public class NetworkStatsManager { /** * Query network usage statistics details. Only usable for uids belonging to calling user. - * Result is aggregated over state but not aggregated over time. + * Result is aggregated over state but not aggregated over time. This means buckets' start and + * end timestamps are going to be between 'startTime' and 'endTime' parameters, state is going + * to be {@link NetworkStats.Bucket#STATE_ALL} and uid the same as the 'uid' parameter. * * @param networkType As defined in {@link ConnectivityManager}, e.g. * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} @@ -172,15 +186,15 @@ public class NetworkStatsManager { * @return Statistics object or null if permissions are insufficient or error happened during * statistics collection. */ - public NetworkUsageStats queryDetailsForUid(int networkType, String subscriberId, + public NetworkStats queryDetailsForUid(int networkType, String subscriberId, long startTime, long endTime, int uid) throws SecurityException, RemoteException { NetworkTemplate template = createTemplate(networkType, subscriberId); if (template == null) { return null; } - NetworkUsageStats result; - result = new NetworkUsageStats(mContext, template, startTime, endTime); + NetworkStats result; + result = new NetworkStats(mContext, template, startTime, endTime); result.startHistoryEnumeration(uid); return result; @@ -188,7 +202,9 @@ public class NetworkStatsManager { /** * Query network usage statistics details. Result filtered to include only uids belonging to - * calling user. Result is aggregated over state but not aggregated over time or uid. + * calling user. Result is aggregated over state but not aggregated over time or uid. This means + * buckets' start and end timestamps are going to be between 'startTime' and 'endTime' + * parameters, state is going to be {@link NetworkStats.Bucket#STATE_ALL} and uid will vary. * * @param networkType As defined in {@link ConnectivityManager}, e.g. * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI} @@ -201,14 +217,14 @@ public class NetworkStatsManager { * @return Statistics object or null if permissions are insufficient or error happened during * statistics collection. */ - public NetworkUsageStats queryDetails(int networkType, String subscriberId, long startTime, + public NetworkStats queryDetails(int networkType, String subscriberId, long startTime, long endTime) throws SecurityException, RemoteException { NetworkTemplate template = createTemplate(networkType, subscriberId); if (template == null) { return null; } - NetworkUsageStats result; - result = new NetworkUsageStats(mContext, template, startTime, endTime); + NetworkStats result; + result = new NetworkStats(mContext, template, startTime, endTime); result.startUserUidEnumeration(); return result; } diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java index abfc435..81c7422 100644 --- a/core/java/android/app/usage/UsageStats.java +++ b/core/java/android/app/usage/UsageStats.java @@ -46,6 +46,13 @@ public final class UsageStats implements Parcelable { public long mLastTimeUsed; /** + * Last time the package was used and the beginning of the idle countdown. + * This uses a different timebase that is about how much the device has been in use in general. + * {@hide} + */ + public long mBeginIdleTime; + + /** * {@hide} */ public long mTotalTimeInForeground; @@ -74,6 +81,7 @@ public final class UsageStats implements Parcelable { mTotalTimeInForeground = stats.mTotalTimeInForeground; mLaunchCount = stats.mLaunchCount; mLastEvent = stats.mLastEvent; + mBeginIdleTime = stats.mBeginIdleTime; } public String getPackageName() { @@ -110,6 +118,15 @@ public final class UsageStats implements Parcelable { } /** + * @hide + * Get the last time this package was active, measured in milliseconds. This timestamp + * uses a timebase that represents how much the device was used and not wallclock time. + */ + public long getBeginIdleTime() { + return mBeginIdleTime; + } + + /** * Get the total time this package spent in the foreground, measured in milliseconds. */ public long getTotalTimeInForeground() { @@ -133,6 +150,7 @@ public final class UsageStats implements Parcelable { mLastEvent = right.mLastEvent; mEndTimeStamp = right.mEndTimeStamp; mLastTimeUsed = right.mLastTimeUsed; + mBeginIdleTime = right.mBeginIdleTime; } mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp); mTotalTimeInForeground += right.mTotalTimeInForeground; @@ -153,6 +171,7 @@ public final class UsageStats implements Parcelable { dest.writeLong(mTotalTimeInForeground); dest.writeInt(mLaunchCount); dest.writeInt(mLastEvent); + dest.writeLong(mBeginIdleTime); } public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() { @@ -166,6 +185,7 @@ public final class UsageStats implements Parcelable { stats.mTotalTimeInForeground = in.readLong(); stats.mLaunchCount = in.readInt(); stats.mLastEvent = in.readInt(); + stats.mBeginIdleTime = in.readLong(); return stats; } diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index c74bbdd..34699d8 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -234,4 +234,15 @@ public final class UsageStatsManager { } return false; } + + /** + * @hide + */ + public void setAppInactive(String packageName, boolean inactive) { + try { + mService.setAppInactive(packageName, inactive, UserHandle.myUserId()); + } catch (RemoteException e) { + // fall through + } + } } diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index 8b3fc2e..7bcc038 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -69,14 +69,6 @@ public abstract class UsageStatsManagerInternal { public abstract boolean isAppIdle(String packageName, int userId); /** - * Returns the most recent time that the specified package was active for the given user. - * @param packageName The package to search. - * @param userId The user id of the user of interest. - * @return The timestamp of when the package was last used, or -1 if it hasn't been used. - */ - public abstract long getLastPackageAccessTime(String packageName, int userId); - - /** * Sets up a listener for changes to packages being accessed. * @param listener A listener within the system process. */ diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index 767f59e..f66b5ff 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.Manifest; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; @@ -380,6 +382,7 @@ public final class BluetoothA2dp implements BluetoothProfile { * @return priority of the device * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getPriority(BluetoothDevice device) { if (VDBG) log("getPriority(" + device + ")"); if (mService != null && isEnabled() diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index ec6f18d..8768f40 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -17,6 +17,9 @@ package android.bluetooth; +import android.Manifest; +import android.annotation.IntDef; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; @@ -37,6 +40,8 @@ import android.util.Log; import android.util.Pair; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -133,6 +138,12 @@ public final class BluetoothAdapter { public static final String EXTRA_PREVIOUS_STATE = "android.bluetooth.adapter.extra.PREVIOUS_STATE"; + /** @hide */ + @IntDef({STATE_OFF, STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, STATE_BLE_TURNING_ON, + STATE_BLE_ON, STATE_BLE_TURNING_OFF}) + @Retention(RetentionPolicy.SOURCE) + public @interface AdapterState {} + /** * Indicates the local Bluetooth adapter is off. */ @@ -273,6 +284,11 @@ public final class BluetoothAdapter { public static final String EXTRA_PREVIOUS_SCAN_MODE = "android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE"; + /** @hide */ + @IntDef({SCAN_MODE_NONE, SCAN_MODE_CONNECTABLE, SCAN_MODE_CONNECTABLE_DISCOVERABLE}) + @Retention(RetentionPolicy.SOURCE) + public @interface ScanMode {} + /** * Indicates that both inquiry scan and page scan are disabled on the local * Bluetooth adapter. Therefore this device is neither discoverable @@ -578,6 +594,7 @@ public final class BluetoothAdapter { * * @return true if the local adapter is turned on */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isEnabled() { try { synchronized(mManagerCallback) { @@ -752,6 +769,8 @@ public final class BluetoothAdapter { * * @return current state of Bluetooth adapter */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + @AdapterState public int getState() { try { synchronized(mManagerCallback) { @@ -792,6 +811,8 @@ public final class BluetoothAdapter { * @return current state of Bluetooth adapter * @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + @AdapterState public int getLeState() { try { synchronized(mManagerCallback) { @@ -845,6 +866,7 @@ public final class BluetoothAdapter { * @return true to indicate adapter startup has begun, or false on * immediate error */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean enable() { int state = STATE_OFF; if (isEnabled() == true){ @@ -893,6 +915,7 @@ public final class BluetoothAdapter { * @return true to indicate adapter shutdown has begun, or false on * immediate error */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disable() { try { return mManagerService.disable(true); @@ -925,6 +948,7 @@ public final class BluetoothAdapter { * * @return Bluetooth hardware address as string */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public String getAddress() { try { return mManagerService.getAddress(); @@ -998,6 +1022,7 @@ public final class BluetoothAdapter { * @param name a valid Bluetooth name * @return true if the name was set, false otherwise */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean setName(String name) { if (getState() != STATE_ON) return false; try { @@ -1024,6 +1049,8 @@ public final class BluetoothAdapter { * * @return scan mode */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + @ScanMode public int getScanMode() { if (getState() != STATE_ON) return SCAN_MODE_NONE; try { @@ -1062,7 +1089,7 @@ public final class BluetoothAdapter { * @return true if the scan mode was set, false otherwise * @hide */ - public boolean setScanMode(int mode, int duration) { + public boolean setScanMode(@ScanMode int mode, int duration) { if (getState() != STATE_ON) return false; try { synchronized(mManagerCallback) { @@ -1130,6 +1157,7 @@ public final class BluetoothAdapter { * * @return true on success, false on error */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean startDiscovery() { if (getState() != STATE_ON) return false; try { @@ -1157,6 +1185,7 @@ public final class BluetoothAdapter { * * @return true on success, false on error */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelDiscovery() { if (getState() != STATE_ON) return false; try { @@ -1186,6 +1215,7 @@ public final class BluetoothAdapter { * * @return true if discovering */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean isDiscovering() { if (getState() != STATE_ON) return false; try { @@ -1345,6 +1375,7 @@ public final class BluetoothAdapter { * * @return unmodifiable set of {@link BluetoothDevice}, or null on error */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public Set<BluetoothDevice> getBondedDevices() { if (getState() != STATE_ON) { return toDeviceSet(new BluetoothDevice[0]); @@ -1396,6 +1427,7 @@ public final class BluetoothAdapter { * {@link BluetoothProfile#STATE_CONNECTED}, * {@link BluetoothProfile#STATE_DISCONNECTING} */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getProfileConnectionState(int profile) { if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED; try { @@ -1460,6 +1492,7 @@ public final class BluetoothAdapter { * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions, or channel in use. */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, true, true); @@ -1491,6 +1524,7 @@ public final class BluetoothAdapter { * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions, or channel in use. */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid) throws IOException { return createNewRfcommSocketAndRecord(name, uuid, false, false); @@ -2032,6 +2066,7 @@ public final class BluetoothAdapter { * instead. */ @Deprecated + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean startLeScan(LeScanCallback callback) { return startLeScan(null, callback); } @@ -2052,6 +2087,7 @@ public final class BluetoothAdapter { * instead. */ @Deprecated + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) { if (DBG) Log.d(TAG, "startLeScan(): " + serviceUuids); if (callback == null) { @@ -2138,6 +2174,7 @@ public final class BluetoothAdapter { * @deprecated Use {@link BluetoothLeScanner#stopScan(ScanCallback)} instead. */ @Deprecated + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void stopLeScan(LeScanCallback callback) { if (DBG) Log.d(TAG, "stopLeScan()"); BluetoothLeScanner scanner = getBluetoothLeScanner(); diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index bfc374f..26a91e4 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.Manifest; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; @@ -709,6 +711,7 @@ public final class BluetoothDevice implements Parcelable { * * @return the Bluetooth name, or null if there was a problem. */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public String getName() { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot get Remote Device name"); @@ -729,6 +732,7 @@ public final class BluetoothDevice implements Parcelable { * {@link #DEVICE_TYPE_DUAL}. * {@link #DEVICE_TYPE_UNKNOWN} if it's not available */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getType() { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot get Remote Device type"); @@ -807,6 +811,7 @@ public final class BluetoothDevice implements Parcelable { * * @return false on immediate error, true if bonding will begin */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean createBond() { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device"); @@ -948,6 +953,7 @@ public final class BluetoothDevice implements Parcelable { * * @return the bond state */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getBondState() { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot get bond state"); @@ -1014,6 +1020,7 @@ public final class BluetoothDevice implements Parcelable { * * @return Bluetooth class object, or null on error */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothClass getBluetoothClass() { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot get Bluetooth Class"); @@ -1039,6 +1046,7 @@ public final class BluetoothDevice implements Parcelable { * @return the supported features (UUIDs) of the remote device, * or null on error */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public ParcelUuid[] getUuids() { if (sService == null || isBluetoothEnabled() == false) { Log.e(TAG, "BT not enabled. Cannot get remote device Uuids"); @@ -1065,6 +1073,7 @@ public final class BluetoothDevice implements Parcelable { * of initiating an ACL connection to the remote device * was started. */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean fetchUuidsWithSdp() { IBluetooth service = sService; if (service == null || isBluetoothEnabled() == false) { @@ -1144,6 +1153,7 @@ public final class BluetoothDevice implements Parcelable { * @return true confirmation has been sent out * false for error */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean setPairingConfirmation(boolean confirm) { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot set pairing confirmation"); @@ -1405,6 +1415,7 @@ public final class BluetoothDevice implements Parcelable { * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException { if (isBluetoothEnabled() == false) { Log.e(TAG, "Bluetooth is not enabled"); @@ -1443,6 +1454,7 @@ public final class BluetoothDevice implements Parcelable { * @throws IOException on error, for example Bluetooth not available, or * insufficient permissions */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException { if (isBluetoothEnabled() == false) { Log.e(TAG, "Bluetooth is not enabled"); diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java index b1618cf3..e355a1c 100644 --- a/core/java/android/bluetooth/BluetoothManager.java +++ b/core/java/android/bluetooth/BluetoothManager.java @@ -16,6 +16,8 @@ package android.bluetooth; +import android.Manifest; +import android.annotation.RequiresPermission; import android.content.Context; import android.os.RemoteException; import android.util.Log; @@ -89,6 +91,7 @@ public final class BluetoothManager { * {@link BluetoothProfile#STATE_DISCONNECTED}, * {@link BluetoothProfile#STATE_DISCONNECTING} */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getConnectionState(BluetoothDevice device, int profile) { if (DBG) Log.d(TAG,"getConnectionState()"); @@ -117,6 +120,7 @@ public final class BluetoothManager { * @param profile GATT or GATT_SERVER * @return List of devices. The list will be empty on error. */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public List<BluetoothDevice> getConnectedDevices(int profile) { if (DBG) Log.d(TAG,"getConnectedDevices"); if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) { @@ -161,6 +165,7 @@ public final class BluetoothManager { * {@link BluetoothProfile#STATE_DISCONNECTING}, * @return List of devices. The list will be empty on error. */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public List<BluetoothDevice> getDevicesMatchingConnectionStates(int profile, int[] states) { if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates"); diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index eecb073..cbce22c 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -17,6 +17,9 @@ package android.bluetooth; +import android.Manifest; +import android.annotation.RequiresPermission; + import java.util.List; /** @@ -163,6 +166,7 @@ public interface BluetoothProfile { * * @return List of devices. The list will be empty on error. */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public List<BluetoothDevice> getConnectedDevices(); /** @@ -179,6 +183,7 @@ public interface BluetoothProfile { * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}, * @return List of devices. The list will be empty on error. */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states); /** @@ -191,6 +196,7 @@ public interface BluetoothProfile { * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING} */ + @RequiresPermission(Manifest.permission.BLUETOOTH) public int getConnectionState(BluetoothDevice device); /** diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index a3eceb5..7a894ae 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -101,8 +101,8 @@ interface IBluetooth void getActivityEnergyInfoFromController(); BluetoothActivityEnergyInfo reportActivityInfo(); - // for dumpsys support - String dump(); + // For dumpsys support + void dump(in ParcelFileDescriptor fd); void onLeServiceUp(); void onBrEdrDown(); } diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java index 687bd5d..2e6c4f0 100644 --- a/core/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java @@ -16,6 +16,8 @@ package android.bluetooth.le; +import android.Manifest; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothGatt; @@ -80,6 +82,7 @@ public final class BluetoothLeScanner { * @param callback Callback used to deliver scan results. * @throws IllegalArgumentException If {@code callback} is null. */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void startScan(final ScanCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback is null"); @@ -97,6 +100,7 @@ public final class BluetoothLeScanner { * @param callback Callback used to deliver scan results. * @throws IllegalArgumentException If {@code settings} or {@code callback} is null. */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void startScan(List<ScanFilter> filters, ScanSettings settings, final ScanCallback callback) { startScan(filters, settings, callback, null); @@ -151,6 +155,7 @@ public final class BluetoothLeScanner { * * @param callback */ + @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public void stopScan(ScanCallback callback) { BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter); synchronized (mLeScanClients) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 6a98950..a434c7b 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -134,7 +134,15 @@ public abstract class Context { * explicitly set if desired. * * @see #getSharedPreferences + * + * @deprecated MODE_MULTI_PROCESS does not work reliably in + * some versions of Android, and furthermore does not provide any + * mechanism for reconciling concurrent modifications across + * processes. Applications should not attempt to use it. Instead, + * they should use an explicit cross-process data management + * approach such as {@link android.content.ContentProvider ContentProvider}. */ + @Deprecated public static final int MODE_MULTI_PROCESS = 0x0004; /** @@ -604,11 +612,7 @@ public abstract class Context { * editor (SharedPreferences.edit()) and then commit changes (Editor.commit()). * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the * default operation, {@link #MODE_WORLD_READABLE} - * and {@link #MODE_WORLD_WRITEABLE} to control permissions. The bit - * {@link #MODE_MULTI_PROCESS} can also be used if multiple processes - * are mutating the same SharedPreferences file. {@link #MODE_MULTI_PROCESS} - * is always on in apps targeting Gingerbread (Android 2.3) and below, and - * off by default in later versions. + * and {@link #MODE_WORLD_WRITEABLE} to control permissions. * * @return The single {@link SharedPreferences} instance that can be used * to retrieve and modify the preference values. @@ -616,7 +620,6 @@ public abstract class Context { * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE * @see #MODE_WORLD_WRITEABLE - * @see #MODE_MULTI_PROCESS */ public abstract SharedPreferences getSharedPreferences(String name, int mode); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 7d76760..2db623b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1535,6 +1535,22 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.MANAGE_APP_PERMISSIONS"; /** + * Activity action: Launch UI to manage permissions. + * <p> + * Input: Nothing. + * </p> + * <p> + * Output: Nothing. + * </p> + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_MANAGE_PERMISSIONS = + "android.intent.action.MANAGE_PERMISSIONS"; + + /** * Intent extra: An app package name. * <p> * Type: String diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 707ef30..96bb2ee 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -107,6 +107,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * * @see {@link android.content.Context#getNoBackupFilesDir} * @see {@link #FLAG_ALLOW_BACKUP} + * + * @hide */ public int fullBackupContent = 0; diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 94b0223..ddff782 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -96,9 +96,14 @@ interface IPackageManager { void removePermission(String name); - void grantPermission(String packageName, String permissionName, int userId); + void grantRuntimePermission(String packageName, String permissionName, int userId); - void revokePermission(String packageName, String permissionName, int userId); + void revokeRuntimePermission(String packageName, String permissionName, int userId); + + int getPermissionFlags(String permissionName, String packageName, int userId); + + void updatePermissionFlags(String permissionName, String packageName, int flagMask, + int flagValues, int userId); boolean isProtectedBroadcast(String actionName); diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java index 87b97aa..6827d7a 100644 --- a/core/java/android/content/pm/LauncherActivityInfo.java +++ b/core/java/android/content/pm/LauncherActivityInfo.java @@ -103,20 +103,30 @@ public class LauncherActivityInfo { * density DPI values from {@link DisplayMetrics}. * @see #getBadgedIcon(int) * @see DisplayMetrics - * @return The drawable associated with the activity + * @return The drawable associated with the activity. */ public Drawable getIcon(int density) { - int iconRes = mResolveInfo.getIconResource(); - Resources resources = null; - Drawable icon = null; - // Get the preferred density icon from the app's resources - if (density != 0 && iconRes != 0) { - try { - resources = mPm.getResourcesForApplication(mActivityInfo.applicationInfo); - icon = resources.getDrawableForDensity(iconRes, density); - } catch (NameNotFoundException | Resources.NotFoundException exc) { - } + final int iconRes = mResolveInfo.getIconResource(); + Drawable icon = getDrawableForDensity(iconRes, density); + // Get the default density icon + if (icon == null) { + icon = mResolveInfo.loadIcon(mPm); } + return icon; + } + + /** + * Returns the icon for this activity, without any badging for the profile. + * This function can get the icon no matter the icon needs to be badged or not. + * @param density The preferred density of the icon, zero for default density. Use + * density DPI values from {@link DisplayMetrics}. + * @see #getBadgedIcon(int) + * @see DisplayMetrics + * @return The drawable associated with the activity. + */ + private Drawable getOriginalIcon(int density) { + final int iconRes = mResolveInfo.getIconResourceInternal(); + Drawable icon = getDrawableForDensity(iconRes, density); // Get the default density icon if (icon == null) { icon = mResolveInfo.loadIcon(mPm); @@ -125,6 +135,27 @@ public class LauncherActivityInfo { } /** + * Returns the drawable for this activity, without any badging for the profile. + * @param resource id of the drawable. + * @param density The preferred density of the icon, zero for default density. Use + * density DPI values from {@link DisplayMetrics}. + * @see DisplayMetrics + * @return The drawable associated with the resource id. + */ + private Drawable getDrawableForDensity(int iconRes, int density) { + // Get the preferred density icon from the app's resources + if (density != 0 && iconRes != 0) { + try { + final Resources resources + = mPm.getResourcesForApplication(mActivityInfo.applicationInfo); + return resources.getDrawableForDensity(iconRes, density); + } catch (NameNotFoundException | Resources.NotFoundException exc) { + } + } + return null; + } + + /** * Returns the application flags from the ApplicationInfo of the activity. * * @return Application flags @@ -167,7 +198,7 @@ public class LauncherActivityInfo { * @return A badged icon for the activity. */ public Drawable getBadgedIcon(int density) { - Drawable originalIcon = getIcon(density); + Drawable originalIcon = getOriginalIcon(density); if (originalIcon instanceof BitmapDrawable) { return mPm.getUserBadgedIcon(originalIcon, mUser); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 51fa075..2ca0306 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -653,6 +653,16 @@ public abstract class PackageManager { public static final int INSTALL_FAILED_VERSION_DOWNGRADE = -25; /** + * Installation return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if + * the old package has target SDK high enough to support runtime permission and + * the new package has target SDK low enough to not support runtime permissions. + * @hide + */ + @SystemApi + public static final int INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE = -26; + + /** * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} * if the parser was given a path that is not a file, or does not end with the expected @@ -1888,6 +1898,65 @@ public abstract class PackageManager { public static final String EXTRA_FAILURE_EXISTING_PERMISSION = "android.content.pm.extra.FAILURE_EXISTING_PERMISSION"; + /** + * Permission flag: The permission is set in its current state + * by the user and apps can still request it at runtime. + * + * @hide + */ + @SystemApi + public static final int FLAG_PERMISSION_USER_SET = 1 << 0; + + /** + * Permission flag: The permission is set in its current state + * by the user and it is fixed, i.e. apps can no longer request + * this permission. + * + * @hide + */ + @SystemApi + public static final int FLAG_PERMISSION_USER_FIXED = 1 << 1; + + /** + * Permission flag: The permission is set in its current state + * by device policy and neither apps nor the user can change + * its state. + * + * @hide + */ + @SystemApi + public static final int FLAG_PERMISSION_POLICY_FIXED = 1 << 2; + + /** + * Permission flag: The permission is set in a granted state but + * access to resources it guards is restricted by other means to + * enable revoking a permission on legacy apps that do not support + * runtime permissions. If this permission is upgraded to runtime + * because the app was updated to support runtime permissions, the + * the permission will be revoked in the upgrade process. + * + * @hide + */ + @SystemApi + public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 1 << 3; + + /** + * Permission flag: The permission is set in its current state + * because the app is a component that is a part of the system. + * + * @hide + */ + @SystemApi + public static final int FLAG_PERMISSION_SYSTEM_FIXED = 1 << 4; + + /** + * Mask for all permission flags. + * + * @hide + */ + @SystemApi + public static final int MASK_PERMISSION_FLAGS = 0xFF; + /** * Retrieve overall information about an application package that is * installed on the system. @@ -2374,6 +2443,21 @@ public abstract class PackageManager { */ public abstract void removePermission(String name); + + /** + * Permission flags set when granting or revoking a permission. + * + * @hide + */ + @SystemApi + @IntDef({FLAG_PERMISSION_USER_SET, + FLAG_PERMISSION_USER_FIXED, + FLAG_PERMISSION_POLICY_FIXED, + FLAG_PERMISSION_REVOKE_ON_UPGRADE, + FLAG_PERMISSION_SYSTEM_FIXED}) + @Retention(RetentionPolicy.SOURCE) + public @interface PermissionFlags {} + /** * Grant a runtime permission to an application which the application does not * already have. The permission must have been requested by the application. @@ -2389,19 +2473,20 @@ public abstract class PackageManager { * @param permissionName The permission name to grant. * @param user The user for which to grant the permission. * - * @see #revokePermission(String, String, android.os.UserHandle) + * @see #revokeRuntimePermission(String, String, android.os.UserHandle) + * @see android.content.pm.PackageManager.PermissionFlags * * @hide */ @SystemApi - public abstract void grantPermission(@NonNull String packageName, + public abstract void grantRuntimePermission(@NonNull String packageName, @NonNull String permissionName, @NonNull UserHandle user); /** * Revoke a runtime permission that was previously granted by {@link - * #grantPermission(String, String, android.os.UserHandle)}. The permission - * must have been requested by and granted to the application. If the - * application is not allowed to hold the permission, a {@link + * #grantRuntimePermission(String, String, android.os.UserHandle)}. The + * permission must have been requested by and granted to the application. + * If the application is not allowed to hold the permission, a {@link * java.lang.SecurityException} is thrown. * <p> * <strong>Note: </strong>Using this API requires holding @@ -2413,15 +2498,47 @@ public abstract class PackageManager { * @param permissionName The permission name to revoke. * @param user The user for which to revoke the permission. * - * @see #grantPermission(String, String, android.os.UserHandle) + * @see #grantRuntimePermission(String, String, android.os.UserHandle) + * @see android.content.pm.PackageManager.PermissionFlags * * @hide */ @SystemApi - public abstract void revokePermission(@NonNull String packageName, + public abstract void revokeRuntimePermission(@NonNull String packageName, @NonNull String permissionName, @NonNull UserHandle user); /** + * Gets the state flags associated with a permission. + * + * @param permissionName The permission for which to get the flags. + * @param packageName The package name for which to get the flags. + * @param user The user for which to get permission flags. + * @return The permission flags. + * + * @hide + */ + @SystemApi + public abstract @PermissionFlags int getPermissionFlags(String permissionName, + String packageName, @NonNull UserHandle user); + + /** + * Updates the flags associated with a permission by replacing the flags in + * the specified mask with the provided flag values. + * + * @param permissionName The permission for which to update the flags. + * @param packageName The package name for which to update the flags. + * @param flagMask The flags which to replace. + * @param flagValues The flags with which to replace. + * @param user The user for which to update the permission flags. + * + * @hide + */ + @SystemApi + public abstract void updatePermissionFlags(String permissionName, + String packageName, @PermissionFlags int flagMask, int flagValues, + @NonNull UserHandle user); + + /** * Returns an {@link android.content.Intent} suitable for passing to * {@link android.app.Activity#startActivityForResult(android.content.Intent, int)} * which prompts the user to grant permissions to this application. @@ -3632,6 +3749,7 @@ public abstract class PackageManager { * * @hide */ + @SystemApi public abstract void verifyIntentFilter(int verificationId, int verificationCode, List<String> outFailedDomains); @@ -4404,6 +4522,7 @@ public abstract class PackageManager { case INSTALL_FAILED_PACKAGE_CHANGED: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_FAILED_UID_CHANGED: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_FAILED_VERSION_DOWNGRADE: return PackageInstaller.STATUS_FAILURE_INVALID; + case INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_PARSE_FAILED_NOT_APK: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_PARSE_FAILED_BAD_MANIFEST: return PackageInstaller.STATUS_FAILURE_INVALID; case INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: return PackageInstaller.STATUS_FAILURE_INVALID; diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index c2d2f65..0bd2a3b 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -52,6 +52,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -573,7 +574,7 @@ public abstract class RegisteredServicesCache<V> { private void readPersistentServicesLocked(InputStream is) throws XmlPullParserException, IOException { XmlPullParser parser = Xml.newPullParser(); - parser.setInput(is, null); + parser.setInput(is, StandardCharsets.UTF_8.name()); int eventType = parser.getEventType(); while (eventType != XmlPullParser.START_TAG && eventType != XmlPullParser.END_DOCUMENT) { @@ -663,7 +664,7 @@ public abstract class RegisteredServicesCache<V> { try { fos = atomicFile.startWrite(); XmlSerializer out = new FastXmlSerializer(); - out.setOutput(fos, "utf-8"); + out.setOutput(fos, StandardCharsets.UTF_8.name()); out.startDocument(null, true); out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); out.startTag(null, "services"); diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java index 05f5e90..649fdb4 100644 --- a/core/java/android/content/pm/ResolveInfo.java +++ b/core/java/android/content/pm/ResolveInfo.java @@ -221,16 +221,16 @@ public class ResolveInfo implements Parcelable { } return ci.loadIcon(pm); } - + /** * Return the icon resource identifier to use for this match. If the * match defines an icon, that is used; else if the activity defines * an icon, that is used; else, the application icon is used. - * + * This function does not check noResourceId flag. + * * @return The icon associated with this match. */ - public final int getIconResource() { - if (noResourceId) return 0; + final int getIconResourceInternal() { if (icon != 0) return icon; final ComponentInfo ci = getComponentInfo(); if (ci != null) { @@ -239,6 +239,18 @@ public class ResolveInfo implements Parcelable { return 0; } + /** + * Return the icon resource identifier to use for this match. If the + * match defines an icon, that is used; else if the activity defines + * an icon, that is used; else, the application icon is used. + * + * @return The icon associated with this match. + */ + public final int getIconResource() { + if (noResourceId) return 0; + return getIconResourceInternal(); + } + public void dump(Printer pw, String prefix) { if (filter != null) { pw.println(prefix + "Filter:"); diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index bc6d4ce..fd60476 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -156,9 +156,34 @@ public final class Configuration implements Parcelable, Comparable<Configuration * value indicating that a layout dir has been set to RTL. */ public static final int SCREENLAYOUT_LAYOUTDIR_RTL = 0x02 << SCREENLAYOUT_LAYOUTDIR_SHIFT; + /** Constant for {@link #screenLayout}: bits that encode roundness of the screen. */ + public static final int SCREENLAYOUT_ROUND_MASK = 0x300; + /** @hide Constant for {@link #screenLayout}: bit shift to get to screen roundness bits */ + public static final int SCREENLAYOUT_ROUND_SHIFT = 8; + /** + * Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_ROUND_MASK} value indicating + * that it is unknown whether or not the screen has a round shape. + */ + public static final int SCREENLAYOUT_ROUND_UNDEFINED = 0x00; + /** + * Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_ROUND_MASK} value indicating + * that the screen does not have a rounded shape. + */ + public static final int SCREENLAYOUT_ROUND_NO = 0x1 << SCREENLAYOUT_ROUND_SHIFT; + /** + * Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_ROUND_MASK} value indicating + * that the screen has a rounded shape. Corners may not be visible to the user; + * developers should pay special attention to the {@link android.view.WindowInsets} delivered + * to views for more information about ensuring content is not obscured. + * + * <p>Corresponds to the <code>-round</code> resource qualifier.</p> + */ + public static final int SCREENLAYOUT_ROUND_YES = 0x2 << SCREENLAYOUT_ROUND_SHIFT; + /** Constant for {@link #screenLayout}: a value indicating that screenLayout is undefined */ public static final int SCREENLAYOUT_UNDEFINED = SCREENLAYOUT_SIZE_UNDEFINED | - SCREENLAYOUT_LONG_UNDEFINED | SCREENLAYOUT_LAYOUTDIR_UNDEFINED; + SCREENLAYOUT_LONG_UNDEFINED | SCREENLAYOUT_LAYOUTDIR_UNDEFINED | + SCREENLAYOUT_ROUND_UNDEFINED; /** * Special flag we generate to indicate that the screen layout requires @@ -174,18 +199,22 @@ public final class Configuration implements Parcelable, Comparable<Configuration * <p>The {@link #SCREENLAYOUT_SIZE_MASK} bits define the overall size * of the screen. They may be one of * {@link #SCREENLAYOUT_SIZE_SMALL}, {@link #SCREENLAYOUT_SIZE_NORMAL}, - * {@link #SCREENLAYOUT_SIZE_LARGE}, or {@link #SCREENLAYOUT_SIZE_XLARGE}. + * {@link #SCREENLAYOUT_SIZE_LARGE}, or {@link #SCREENLAYOUT_SIZE_XLARGE}.</p> * * <p>The {@link #SCREENLAYOUT_LONG_MASK} defines whether the screen * is wider/taller than normal. They may be one of - * {@link #SCREENLAYOUT_LONG_NO} or {@link #SCREENLAYOUT_LONG_YES}. + * {@link #SCREENLAYOUT_LONG_NO} or {@link #SCREENLAYOUT_LONG_YES}.</p> * * <p>The {@link #SCREENLAYOUT_LAYOUTDIR_MASK} defines whether the screen layout * is either LTR or RTL. They may be one of - * {@link #SCREENLAYOUT_LAYOUTDIR_LTR} or {@link #SCREENLAYOUT_LAYOUTDIR_RTL}. + * {@link #SCREENLAYOUT_LAYOUTDIR_LTR} or {@link #SCREENLAYOUT_LAYOUTDIR_RTL}.</p> + * + * <p>The {@link #SCREENLAYOUT_ROUND_MASK} defines whether the screen has a rounded + * shape. They may be one of {@link #SCREENLAYOUT_ROUND_NO} or {@link #SCREENLAYOUT_ROUND_YES}. + * </p> * * <p>See <a href="{@docRoot}guide/practices/screens_support.html">Supporting - * Multiple Screens</a> for more information. + * Multiple Screens</a> for more information.</p> */ public int screenLayout; @@ -1328,6 +1357,16 @@ public final class Configuration implements Parcelable, Comparable<Configuration } /** + * Return whether the screen has a round shape. Apps may choose to change styling based + * on this property, such as the alignment or layout of text or informational icons. + * + * @return true if the screen is rounded, false otherwise + */ + public boolean isScreenRound() { + return (screenLayout & SCREENLAYOUT_ROUND_MASK) == SCREENLAYOUT_ROUND_YES; + } + + /** * * @hide */ @@ -1425,6 +1464,17 @@ public final class Configuration implements Parcelable, Comparable<Configuration break; } + switch (config.screenLayout & Configuration.SCREENLAYOUT_ROUND_MASK) { + case Configuration.SCREENLAYOUT_ROUND_YES: + parts.add("round"); + break; + case Configuration.SCREENLAYOUT_ROUND_NO: + parts.add("notround"); + break; + default: + break; + } + switch (config.orientation) { case Configuration.ORIENTATION_LANDSCAPE: parts.add("land"); @@ -1640,6 +1690,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration delta.screenLayout |= change.screenLayout & SCREENLAYOUT_LONG_MASK; } + if ((base.screenLayout & SCREENLAYOUT_ROUND_MASK) != + (change.screenLayout & SCREENLAYOUT_ROUND_MASK)) { + delta.screenLayout |= change.screenLayout & SCREENLAYOUT_ROUND_MASK; + } + if ((base.uiMode & UI_MODE_TYPE_MASK) != (change.uiMode & UI_MODE_TYPE_MASK)) { delta.uiMode |= change.uiMode & UI_MODE_TYPE_MASK; } diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl index 7b96e20..9201b61 100644 --- a/core/java/android/hardware/ICameraService.aidl +++ b/core/java/android/hardware/ICameraService.aidl @@ -81,5 +81,5 @@ interface ICameraService * * Callers require the android.permission.CAMERA_SEND_SYSTEM_EVENTS permission. */ - oneway void notifySystemEvent(int eventId, int arg0); + oneway void notifySystemEvent(int eventId, in int[] args); } diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java index ef71c42..c6f622c 100644 --- a/core/java/android/hardware/camera2/CameraCaptureSession.java +++ b/core/java/android/hardware/camera2/CameraCaptureSession.java @@ -29,11 +29,11 @@ import java.util.List; * <p>A CameraCaptureSession is created by providing a set of target output surfaces to * {@link CameraDevice#createCaptureSession createCaptureSession}, or by providing an * {@link android.hardware.camera2.params.InputConfiguration} and a set of target output surfaces to - * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession} for a - * reprocessible capture session. Once created, the session is active until a new session is + * {@link CameraDevice#createReprocessableCaptureSession createReprocessableCaptureSession} for a + * reprocessable capture session. Once created, the session is active until a new session is * created by the camera device, or the camera device is closed.</p> * - * <p>All capture sessions can be used for capturing images from the camera but only reprocessible + * <p>All capture sessions can be used for capturing images from the camera but only reprocessable * capture sessions can reprocess images captured from the camera in the same session previously. * </p> * @@ -41,7 +41,7 @@ import java.util.List; * it requires configuring the camera device's internal pipelines and allocating memory buffers for * sending images to the desired targets. Therefore the setup is done asynchronously, and * {@link CameraDevice#createCaptureSession createCaptureSession} and - * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession} will + * {@link CameraDevice#createReprocessableCaptureSession createReprocessableCaptureSession} will * send the ready-to-use CameraCaptureSession to the provided listener's * {@link CameraCaptureSession.StateCallback#onConfigured onConfigured} callback. If configuration * cannot be completed, then the @@ -156,7 +156,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * * <p>All capture sessions can be used for capturing images from the camera but only capture * sessions created by - * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession} + * {@link CameraDevice#createReprocessableCaptureSession createReprocessableCaptureSession} * can submit reprocess capture requests. Submitting a reprocess request to a regular capture * session will result in an {@link IllegalArgumentException}.</p> * @@ -177,19 +177,22 @@ public abstract class CameraCaptureSession implements AutoCloseable { * was explicitly closed, a new session has been created * or the camera device has been closed. * @throws IllegalArgumentException if the request targets no Surfaces or Surfaces that are not - * configured as outputs for this session; or a reprocess - * capture request is submitted in a non-reprocessible capture - * session; or the reprocess capture request was created with - * a {@link TotalCaptureResult} from a different session; or - * the capture targets a Surface in the middle of being - * {@link #prepare prepared}; or the handler is null, the - * listener is not null, and the calling thread has no looper. + * configured as outputs for this session; or the request + * targets a set of Surfaces that cannot be submitted + * simultaneously in a reprocessable capture session; or a + * reprocess capture request is submitted in a + * non-reprocessable capture session; or the reprocess capture + * request was created with a {@link TotalCaptureResult} from + * a different session; or the capture targets a Surface in + * the middle of being {@link #prepare prepared}; or the + * handler is null, the listener is not null, and the calling + * thread has no looper. * * @see #captureBurst * @see #setRepeatingRequest * @see #setRepeatingBurst * @see #abortCaptures - * @see CameraDevice#createReprocessibleCaptureSession + * @see CameraDevice#createReprocessableCaptureSession */ public abstract int capture(CaptureRequest request, CaptureCallback listener, Handler handler) throws CameraAccessException; @@ -211,7 +214,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * * <p>All capture sessions can be used for capturing images from the camera but only capture * sessions created by - * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession} + * {@link CameraDevice#createReprocessableCaptureSession createReprocessableCaptureSession} * can submit reprocess capture requests. Submitting a reprocess request to a regular * capture session will result in an {@link IllegalArgumentException}.</p> * @@ -233,14 +236,16 @@ public abstract class CameraCaptureSession implements AutoCloseable { * was explicitly closed, a new session has been created * or the camera device has been closed. * @throws IllegalArgumentException If the requests target no Surfaces, or the requests target - * Surfaces not currently configured as outputs; or a reprocess - * capture request is submitted in a non-reprocessible capture - * session; or one of the reprocess capture requests was - * created with a {@link TotalCaptureResult} from a different - * session; or one of the captures targets a Surface in the - * middle of being {@link #prepare prepared}; or if the handler - * is null, the listener is not null, and the calling thread - * has no looper. + * Surfaces not currently configured as outputs; or one of the + * requests targets a set of Surfaces that cannot be submitted + * simultaneously in a reprocessable capture session; or a + * reprocess capture request is submitted in a + * non-reprocessable capture session; or one of the reprocess + * capture requests was created with a + * {@link TotalCaptureResult} from a different session; or one + * of the captures targets a Surface in the middle of being + * {@link #prepare prepared}; or if the handler is null, the + * listener is not null, and the calling thread has no looper. * * @see #capture * @see #setRepeatingRequest @@ -420,7 +425,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * * <p>This method is the fastest way to switch the camera device to a new session with * {@link CameraDevice#createCaptureSession} or - * {@link CameraDevice#createReprocessibleCaptureSession}, at the cost of discarding in-progress + * {@link CameraDevice#createReprocessableCaptureSession}, at the cost of discarding in-progress * work. It must be called before the new session is created. Once all pending requests are * either completed or thrown away, the {@link StateCallback#onReady} callback will be called, * if the session has not been closed. Otherwise, the {@link StateCallback#onClosed} @@ -443,7 +448,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * @see #setRepeatingRequest * @see #setRepeatingBurst * @see CameraDevice#createCaptureSession - * @see CameraDevice#createReprocessibleCaptureSession + * @see CameraDevice#createReprocessableCaptureSession */ public abstract void abortCaptures() throws CameraAccessException; @@ -454,14 +459,14 @@ public abstract class CameraCaptureSession implements AutoCloseable { * @return {@code true} if the application can submit reprocess capture requests with this * camera capture session. {@code false} otherwise. * - * @see CameraDevice#createReprocessibleCaptureSession + * @see CameraDevice#createReprocessableCaptureSession */ - public abstract boolean isReprocessible(); + public abstract boolean isReprocessable(); /** - * Get the input Surface associated with a reprocessible capture session. + * Get the input Surface associated with a reprocessable capture session. * - * <p>Each reprocessible capture session has an input {@link Surface} where the reprocess + * <p>Each reprocessable capture session has an input {@link Surface} where the reprocess * capture requests get the input images from, rather than the camera device. The application * can create a {@link android.media.ImageWriter} with this input {@link Surface} and use it to * provide input images for reprocess capture requests.</p> @@ -469,7 +474,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * @return The {@link Surface} where reprocessing capture requests get the input images from. If * this is not a reprocess capture session, {@code null} will be returned. * - * @see CameraDevice#createReprocessibleCaptureSession + * @see CameraDevice#createReprocessableCaptureSession * @see android.media.ImageWriter * @see android.media.ImageReader */ diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 19e821c..d3b63f9 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -1239,7 +1239,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * only the input buffer will be used to produce these output stream buffers, and a * new sensor image will not be captured.</p> * <p>For example, for Zero Shutter Lag (ZSL) still capture use case, the input - * stream image format will be OPAQUE, the associated output stream image format + * stream image format will be PRIVATE, the associated output stream image format * should be JPEG.</p> * <p><b>Range of valid values:</b><br></p> * <p>0 or 1.</p> @@ -1326,7 +1326,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR MANUAL_SENSOR}</li> * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING MANUAL_POST_PROCESSING}</li> * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}</li> - * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING OPAQUE_REPROCESSING}</li> + * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING PRIVATE_REPROCESSING}</li> * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS READ_SENSOR_SETTINGS}</li> * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}</li> * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING YUV_REPROCESSING}</li> @@ -1339,7 +1339,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING * @see #REQUEST_AVAILABLE_CAPABILITIES_RAW - * @see #REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING + * @see #REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING * @see #REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS * @see #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE * @see #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING @@ -1536,12 +1536,12 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <tr> * <td align="left">{@link android.graphics.ImageFormat#PRIVATE }</td> * <td align="left">{@link android.graphics.ImageFormat#JPEG }</td> - * <td align="left">OPAQUE_REPROCESSING</td> + * <td align="left">PRIVATE_REPROCESSING</td> * </tr> * <tr> * <td align="left">{@link android.graphics.ImageFormat#PRIVATE }</td> * <td align="left">{@link android.graphics.ImageFormat#YUV_420_888 }</td> - * <td align="left">OPAQUE_REPROCESSING</td> + * <td align="left">PRIVATE_REPROCESSING</td> * </tr> * <tr> * <td align="left">{@link android.graphics.ImageFormat#YUV_420_888 }</td> @@ -1556,8 +1556,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * </tbody> * </table> * <p>PRIVATE refers to a device-internal format that is not directly application-visible. A - * PRIVATE input surface can be acquired by {@link android.media.ImageReader#newOpaqueInstance }.</p> - * <p>For a OPAQUE_REPROCESSING-capable camera device, using the PRIVATE format as either input + * PRIVATE input surface can be acquired by {@link android.media.ImageReader#newInstance } + * with {@link android.graphics.ImageFormat#PRIVATE } as the format.</p> + * <p>For a PRIVATE_REPROCESSING-capable camera device, using the PRIVATE format as either input * or output will never hurt maximum frame rate (i.e. {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration getOutputStallDuration(ImageFormat.PRIVATE, size)} is always 0),</p> * <p>Attempting to configure an input stream with output streams not * listed as available in this map is not valid.</p> @@ -2647,8 +2648,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * formats/sizes combination.</p> * <p>If this key reports 0, it means a reprocess request doesn't introduce any glitch to the * ongoing camera repeating request outputs, as if this reprocess request is never issued.</p> - * <p>This key is supported if the camera device supports OPAQUE or YUV reprocessing ( - * i.e. {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains OPAQUE_REPROCESSING or + * <p>This key is supported if the camera device supports PRIVATE or YUV reprocessing ( + * i.e. {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains PRIVATE_REPROCESSING or * YUV_REPROCESSING).</p> * <p><b>Units</b>: Number of frames.</p> * <p><b>Range of valid values:</b><br> diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index e9564b3..dad4fb6 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -100,7 +100,7 @@ public abstract class CameraDevice implements AutoCloseable { * means maximizing image quality without compromising preview frame rate. * AE/AWB/AF should be on auto mode. * This template is guaranteed to be supported on camera devices that support the - * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING OPAQUE_REPROCESSING} + * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING PRIVATE_REPROCESSING} * capability or the * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING YUV_REPROCESSING} * capability. @@ -409,31 +409,34 @@ public abstract class CameraDevice implements AutoCloseable { CameraCaptureSession.StateCallback callback, Handler handler) throws CameraAccessException; /** - * Create a new reprocessible camera capture session by providing the desired reprocessing + * Create a new reprocessable camera capture session by providing the desired reprocessing * input Surface configuration and the target output set of Surfaces to the camera device. * * <p>If a camera device supports YUV reprocessing - * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING}) or OPAQUE + * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING}) or PRIVATE * reprocessing - * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING}), besides - * the capture session created via {@link #createCaptureSession}, the application can also - * create a reprocessible capture session to submit reprocess capture requests in addition to - * regular capture requests. A reprocess capture request takes the next available buffer from - * the session's input Surface, and sends it through the camera device's processing pipeline - * again, to produce buffers for the request's target output Surfaces. No new image data is - * captured for a reprocess request. However the input buffer provided by + * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING}), besides + * the capture session created via {@link #createCaptureSession createCaptureSession}, the + * application can also create a reprocessable capture session to submit reprocess capture + * requests in addition to regular capture requests. A reprocess capture request takes the next + * available buffer from the session's input Surface, and sends it through the camera device's + * processing pipeline again, to produce buffers for the request's target output Surfaces. No + * new image data is captured for a reprocess request. However the input buffer provided by * the application must be captured previously by the same camera device in the same session * directly (e.g. for Zero-Shutter-Lag use case) or indirectly (e.g. combining multiple output * images).</p> * - * <p>The active reprocessible capture session determines an input {@link Surface} and the set + * <p>The active reprocessable capture session determines an input {@link Surface} and the set * of potential output Surfaces for the camera devices for each capture request. The application - * can use {@link #createCaptureRequest} to create regular capture requests to capture new - * images from the camera device, and use {@link #createReprocessCaptureRequest} to create - * reprocess capture requests to process buffers from the input {@link Surface}. A request may - * use all or only some of the outputs. All the output Surfaces in one capture request will come - * from the same source, either from a new capture by the camera device, or from the input - * Surface depending on if the request is a reprocess capture request.</p> + * can use {@link #createCaptureRequest createCaptureRequest} to create regular capture requests + * to capture new images from the camera device, and use {@link #createReprocessCaptureRequest + * createReprocessCaptureRequest} to create reprocess capture requests to process buffers from + * the input {@link Surface}. Some combinations of output Surfaces in a session may not be used + * in a request simultaneously. The guaranteed combinations of output Surfaces that can be used + * in a request simultaneously are listed in the tables under {@link #createCaptureSession + * createCaptureSession}. All the output Surfaces in one capture request will come from the + * same source, either from a new capture by the camera device, or from the input Surface + * depending on if the request is a reprocess capture request.</p> * * <p>Input formats and sizes supported by the camera device can be queried via * {@link StreamConfigurationMap#getInputFormats} and @@ -445,11 +448,93 @@ public abstract class CameraDevice implements AutoCloseable { * they cannot be used as targets for a reprocessing request.</p> * * <p>Since the application cannot access {@link android.graphics.ImageFormat#PRIVATE} images - * directly, an output Surface created by {@link android.media.ImageReader#newOpaqueInstance} - * will be considered as intended to be used for reprocessing input and thus the - * {@link android.media.ImageReader} size must match one of the supported input sizes for - * {@link android.graphics.ImageFormat#PRIVATE} format. Otherwise, creating a reprocessible - * capture session will fail.</p> + * directly, an output Surface created by {@link android.media.ImageReader#newInstance} with + * {@link android.graphics.ImageFormat#PRIVATE} as the format will be considered as intended to + * be used for reprocessing input and thus the {@link android.media.ImageReader} size must + * match one of the supported input sizes for {@link android.graphics.ImageFormat#PRIVATE} + * format. Otherwise, creating a reprocessable capture session will fail.</p> + * + * <p>The guaranteed stream configurations listed in + * {@link #createCaptureSession createCaptureSession} are also guaranteed to work for + * {@link #createReprocessableCaptureSession createReprocessableCaptureSession}. In addition, + * the configurations in the tables below are also guaranteed for creating a reprocessable + * capture session if the camera device supports YUV reprocessing or PRIVATE reprocessing. + * However, not all output targets used to create a reprocessable session may be used in a + * {@link CaptureRequest} simultaneously. The guaranteed output targets that can be included + * in a {@link CaptureRequest} simultaneously are listed in the tables under + * {@link #createCaptureSession createCaptureSession}. For example, with a FULL-capability + * ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} {@code == } + * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}) device that supports PRIVATE + * reprocessing, an application can create a reprocessable capture session with 1 input, + * ({@code PRIV}, {@code MAXIMUM}), and 3 outputs, ({@code PRIV}, {@code MAXIMUM}), + * ({@code PRIV}, {@code PREVIEW}), and ({@code YUV}, {@code MAXIMUM}). However, it's not + * guaranteed that an application can submit a regular or reprocess capture with ({@code PRIV}, + * {@code MAXIMUM}) and ({@code YUV}, {@code MAXIMUM}) outputs based on the table listed under + * {@link #createCaptureSession createCaptureSession}. In other words, use the tables below to + * determine the guaranteed stream configurations for creating a reprocessable capture session, + * and use the tables under {@link #createCaptureSession createCaptureSession} to determine the + * guaranteed output targets that can be submitted in a regular or reprocess + * {@link CaptureRequest} simultaneously.</p> + * + * <style scoped> + * #rb { border-right-width: thick; } + * </style> + * + * <p>Limited-capability ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} + * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED}) devices + * support at least the following stream combinations for creating a reprocessable capture + * session in addition to those listed in {@link #createCaptureSession createCaptureSession} for + * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices: + * + * <table> + * <tr><th colspan="11">LIMITED-level additional guaranteed configurations for creating a reprocessable capture session<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr> + * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr> + * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td></td><td id="rb"></td> <td>No-viewfinder still image reprocessing.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>ZSL(Zero-Shutter-Lag) still imaging.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>ZSL still and in-app processing imaging.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td>ZSL in-app processing with still capture.</td> </tr> + * </table><br> + * </p> + * + * <p>FULL-capability ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} + * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}) devices + * support at least the following stream combinations for creating a reprocessable capture + * session in addition to those for + * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices: + * + * <table> + * <tr><th colspan="11">FULL-capability additional guaranteed configurations for creating a reprocessable capture session<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr> + * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr> + * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> + * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td></td><td id="rb"></td> <td></td><td id="rb"></td> <td>Maximum-resolution multi-frame image fusion in-app processing with regular preview.</td> </tr> + * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td></td><td id="rb"></td> <td></td><td id="rb"></td> <td>Maximum-resolution multi-frame image fusion two-input in-app processing.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code RECORD}</td> <td></td><td id="rb"></td> <td>High-resolution ZSL in-app video processing with regular preview.</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>Maximum-resolution ZSL in-app processing with regular preview.</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>Maximum-resolution two-input ZSL in-app processing.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code RECORD}</td> <td>{@code JPEG}</td><td id="rb">{@code RECORD}</td> <td>High-resolution ZSL in-app video processing and video snapshot with regular preview.</td> </tr> + * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution two-input ZSL in-app processing with regular preview.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td>ZSL still capture and in-app processing.</td> </tr> + * </table><br> + * </p> + * + * <p>RAW-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes + * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}) devices additionally support + * at least the following stream combinations for creating a reprocessable capture session + * on both {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL} and + * {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED LIMITED} devices + * + * <table> + * <tr><th colspan="11">RAW-capability additional guaranteed configurations for creating a reprocessable capture session<br>({@code PRIV} input is guaranteed only if PRIVATE reprocessing is supported. {@code YUV} input is guaranteed only if YUV reprocessing is supported)</th></tr> + * <tr><th colspan="2" id="rb">Input</th><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th colspan="2" id="rb">Target 3</th><th colspan="2" id="rb">Target 4</th><th rowspan="2">Sample use case(s)</th> </tr> + * <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th></tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>Mutually exclusive ZSL in-app processing and DNG capture.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>Mutually exclusive ZSL in-app processing and preview with DNG capture.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>Mutually exclusive ZSL two-input in-app processing and DNG capture.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>Mutually exclusive ZSL still capture and preview with DNG capture.</td> </tr> + * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code RAW}</td><td id="rb">{@code MAXIMUM}</td> <td>Mutually exclusive ZSL in-app processing with still capture and DNG capture.</td> </tr> + * </table><br> + * </p> * * @param inputConfig The configuration for the input {@link Surface} * @param outputs The new set of Surfaces that should be made available as @@ -466,6 +551,7 @@ public abstract class CameraDevice implements AutoCloseable { * encountered a fatal error * @throws IllegalStateException if the camera device has been closed * + * @see #createCaptureSession * @see CameraCaptureSession * @see StreamConfigurationMap#getInputFormats * @see StreamConfigurationMap#getInputSizes @@ -474,7 +560,7 @@ public abstract class CameraDevice implements AutoCloseable { * @see android.media.ImageWriter * @see android.media.ImageReader */ - public abstract void createReprocessibleCaptureSession(InputConfiguration inputConfig, + public abstract void createReprocessableCaptureSession(InputConfiguration inputConfig, List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler) throws CameraAccessException; @@ -516,8 +602,7 @@ public abstract class CameraDevice implements AutoCloseable { * {@link CameraCaptureSession}'s input {@link Surface} to all output {@link Surface Surfaces} * included in the reprocess capture request. The reprocess input images must be generated from * one or multiple output images captured from the same camera device. The application can - * provide input images to camera device via - * {{@link android.media.ImageWriter#queueInputImage ImageWriter#queueInputImage}}. + * provide input images to camera device via {@link android.media.ImageWriter#queueInputImage}. * The application must use the capture result of one of those output images to create a * reprocess capture request so that the camera device can use the information to achieve * optimal reprocess image quality. @@ -532,7 +617,7 @@ public abstract class CameraDevice implements AutoCloseable { * * @see CaptureRequest.Builder * @see TotalCaptureResult - * @see CameraDevice#createReprocessibleCaptureSession + * @see CameraDevice#createReprocessableCaptureSession * @see android.media.ImageWriter */ public abstract CaptureRequest.Builder createReprocessCaptureRequest( diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 9327f00..d99cce7 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -556,7 +556,7 @@ public final class CameraManager { * {@link CameraManager#registerTorchCallback} to be notified of such status changes. * </p> * - * @see registerTorchCallback + * @see #registerTorchCallback */ public static abstract class TorchCallback { /** diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index ca9439b..6baa660 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -388,8 +388,8 @@ public abstract class CameraMetadata<TKey> { * <li>{@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}</li> * <li>{@link CaptureRequest#TONEMAP_MODE android.tonemap.mode}</li> * <li>{@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}</li> - * <li>android.tonemap.gamma</li> - * <li>android.tonemap.presetCurve</li> + * <li>{@link CaptureRequest#TONEMAP_GAMMA android.tonemap.gamma}</li> + * <li>{@link CaptureRequest#TONEMAP_PRESET_CURVE android.tonemap.presetCurve}</li> * </ul> * </li> * <li> @@ -429,8 +429,10 @@ public abstract class CameraMetadata<TKey> { * @see CaptureRequest#SHADING_MODE * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE * @see CaptureRequest#TONEMAP_CURVE + * @see CaptureRequest#TONEMAP_GAMMA * @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS * @see CaptureRequest#TONEMAP_MODE + * @see CaptureRequest#TONEMAP_PRESET_CURVE * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES */ public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; @@ -472,7 +474,7 @@ public abstract class CameraMetadata<TKey> { * <li>{@link android.graphics.ImageFormat#PRIVATE } will be reprocessable into both * {@link android.graphics.ImageFormat#YUV_420_888 } and * {@link android.graphics.ImageFormat#JPEG } formats.</li> - * <li>The maximum available resolution for OPAQUE streams + * <li>The maximum available resolution for PRIVATE streams * (both input/output) will match the maximum available * resolution of JPEG streams.</li> * <li>Static metadata {@link CameraCharacteristics#REPROCESS_MAX_CAPTURE_STALL android.reprocess.maxCaptureStall}.</li> @@ -492,7 +494,7 @@ public abstract class CameraMetadata<TKey> { * @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES */ - public static final int REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING = 4; + public static final int REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING = 4; /** * <p>The camera device supports accurately reporting the sensor settings for many of @@ -565,7 +567,7 @@ public abstract class CameraMetadata<TKey> { /** * <p>The camera device supports the YUV_420_888 reprocessing use case, similar as - * OPAQUE_REPROCESSING, This capability requires the camera device to support the + * PRIVATE_REPROCESSING, This capability requires the camera device to support the * following:</p> * <ul> * <li>One input stream is supported, that is, <code>{@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} == 1</code>.</li> @@ -2185,22 +2187,26 @@ public abstract class CameraMetadata<TKey> { public static final int TONEMAP_MODE_HIGH_QUALITY = 2; /** - * <p>Use the gamma value specified in android.tonemap.gamma to peform + * <p>Use the gamma value specified in {@link CaptureRequest#TONEMAP_GAMMA android.tonemap.gamma} to peform * tonemapping.</p> * <p>All color enhancement and tonemapping must be disabled, except - * for applying the tonemapping curve specified by android.tonemap.gamma.</p> + * for applying the tonemapping curve specified by {@link CaptureRequest#TONEMAP_GAMMA android.tonemap.gamma}.</p> * <p>Must not slow down frame rate relative to raw sensor output.</p> + * + * @see CaptureRequest#TONEMAP_GAMMA * @see CaptureRequest#TONEMAP_MODE */ public static final int TONEMAP_MODE_GAMMA_VALUE = 3; /** * <p>Use the preset tonemapping curve specified in - * android.tonemap.presetCurve to peform tonemapping.</p> + * {@link CaptureRequest#TONEMAP_PRESET_CURVE android.tonemap.presetCurve} to peform tonemapping.</p> * <p>All color enhancement and tonemapping must be disabled, except * for applying the tonemapping curve specified by - * android.tonemap.presetCurve.</p> + * {@link CaptureRequest#TONEMAP_PRESET_CURVE android.tonemap.presetCurve}.</p> * <p>Must not slow down frame rate relative to raw sensor output.</p> + * + * @see CaptureRequest#TONEMAP_PRESET_CURVE * @see CaptureRequest#TONEMAP_MODE */ public static final int TONEMAP_MODE_PRESET_CURVE = 4; diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index ab6ce91..5fb9abc 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -158,9 +158,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> private final HashSet<Surface> mSurfaceSet; private final CameraMetadataNative mSettings; private boolean mIsReprocess; - // Each reprocess request must be tied to a reprocessible session ID. + // Each reprocess request must be tied to a reprocessable session ID. // Valid only for reprocess requests (mIsReprocess == true). - private int mReprocessibleSessionId; + private int mReprocessableSessionId; private Object mUserTag; @@ -173,7 +173,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> mSettings = new CameraMetadataNative(); mSurfaceSet = new HashSet<Surface>(); mIsReprocess = false; - mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE; + mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE; } /** @@ -186,7 +186,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> mSettings = new CameraMetadataNative(source.mSettings); mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone(); mIsReprocess = source.mIsReprocess; - mReprocessibleSessionId = source.mReprocessibleSessionId; + mReprocessableSessionId = source.mReprocessableSessionId; mUserTag = source.mUserTag; } @@ -199,30 +199,30 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * @param isReprocess Indicates whether to create a reprocess capture request. {@code true} * to create a reprocess capture request. {@code false} to create a regular * capture request. - * @param reprocessibleSessionId The ID of the camera capture session this capture is created + * @param reprocessableSessionId The ID of the camera capture session this capture is created * for. This is used to validate if the application submits a * reprocess capture request to the same session where * the {@link TotalCaptureResult}, used to create the reprocess * capture, came from. * * @throws IllegalArgumentException If creating a reprocess capture request with an invalid - * reprocessibleSessionId. + * reprocessableSessionId. * * @see CameraDevice#createReprocessCaptureRequest */ private CaptureRequest(CameraMetadataNative settings, boolean isReprocess, - int reprocessibleSessionId) { + int reprocessableSessionId) { mSettings = CameraMetadataNative.move(settings); mSurfaceSet = new HashSet<Surface>(); mIsReprocess = isReprocess; if (isReprocess) { - if (reprocessibleSessionId == CameraCaptureSession.SESSION_ID_NONE) { + if (reprocessableSessionId == CameraCaptureSession.SESSION_ID_NONE) { throw new IllegalArgumentException("Create a reprocess capture request with an " + - "invalid session ID: " + reprocessibleSessionId); + "invalid session ID: " + reprocessableSessionId); } - mReprocessibleSessionId = reprocessibleSessionId; + mReprocessableSessionId = reprocessableSessionId; } else { - mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE; + mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE; } } @@ -307,20 +307,20 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> } /** - * Get the reprocessible session ID this reprocess capture request is associated with. + * Get the reprocessable session ID this reprocess capture request is associated with. * - * @return the reprocessible session ID this reprocess capture request is associated with + * @return the reprocessable session ID this reprocess capture request is associated with * * @throws IllegalStateException if this capture request is not a reprocess capture request. * @hide */ - public int getReprocessibleSessionId() { + public int getReprocessableSessionId() { if (mIsReprocess == false || - mReprocessibleSessionId == CameraCaptureSession.SESSION_ID_NONE) { - throw new IllegalStateException("Getting the reprocessible session ID for a "+ + mReprocessableSessionId == CameraCaptureSession.SESSION_ID_NONE) { + throw new IllegalStateException("Getting the reprocessable session ID for a "+ "non-reprocess capture request is illegal."); } - return mReprocessibleSessionId; + return mReprocessableSessionId; } /** @@ -346,7 +346,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> && mSurfaceSet.equals(other.mSurfaceSet) && mSettings.equals(other.mSettings) && mIsReprocess == other.mIsReprocess - && mReprocessibleSessionId == other.mReprocessibleSessionId; + && mReprocessableSessionId == other.mReprocessableSessionId; } @Override @@ -395,7 +395,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> } mIsReprocess = (in.readInt() == 0) ? false : true; - mReprocessibleSessionId = CameraCaptureSession.SESSION_ID_NONE; + mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE; } @Override @@ -450,19 +450,19 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * @param reprocess Indicates whether to create a reprocess capture request. {@code true} * to create a reprocess capture request. {@code false} to create a regular * capture request. - * @param reprocessibleSessionId The ID of the camera capture session this capture is + * @param reprocessableSessionId The ID of the camera capture session this capture is * created for. This is used to validate if the application * submits a reprocess capture request to the same session * where the {@link TotalCaptureResult}, used to create the * reprocess capture, came from. * * @throws IllegalArgumentException If creating a reprocess capture request with an invalid - * reprocessibleSessionId. + * reprocessableSessionId. * @hide */ public Builder(CameraMetadataNative template, boolean reprocess, - int reprocessibleSessionId) { - mRequest = new CaptureRequest(template, reprocess, reprocessibleSessionId); + int reprocessableSessionId) { + mRequest = new CaptureRequest(template, reprocess, reprocessableSessionId); } /** @@ -1275,7 +1275,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <p>This control (except for MANUAL) is only effective if * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p> * <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} - * contains OPAQUE_REPROCESSING or YUV_REPROCESSING. MANUAL will be supported if + * contains PRIVATE_REPROCESSING or YUV_REPROCESSING. MANUAL will be supported if * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_SENSOR. Other intent values are * always supported.</p> * <p><b>Possible values:</b> @@ -1355,7 +1355,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * android.control.* are mostly disabled, and the camera device implements * one of the scene mode settings (such as ACTION, SUNSET, or PARTY) * as it wishes. The camera device scene mode 3A settings are provided by - * android.control.sceneModeOverrides.</p> + * {@link android.hardware.camera2.CaptureResult capture results}.</p> * <p>When set to OFF_KEEP_STATE, it is similar to OFF mode, the only difference * is that this frame will not be used by camera device background 3A statistics * update, as if this frame is never captured. This mode can be used in the scenario diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 3dc8970..0277c5b 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -1698,7 +1698,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>This control (except for MANUAL) is only effective if * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p> * <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} - * contains OPAQUE_REPROCESSING or YUV_REPROCESSING. MANUAL will be supported if + * contains PRIVATE_REPROCESSING or YUV_REPROCESSING. MANUAL will be supported if * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_SENSOR. Other intent values are * always supported.</p> * <p><b>Possible values:</b> @@ -1921,7 +1921,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * android.control.* are mostly disabled, and the camera device implements * one of the scene mode settings (such as ACTION, SUNSET, or PARTY) * as it wishes. The camera device scene mode 3A settings are provided by - * android.control.sceneModeOverrides.</p> + * {@link android.hardware.camera2.CaptureResult capture results}.</p> * <p>When set to OFF_KEEP_STATE, it is similar to OFF mode, the only difference * is that this frame will not be used by camera device background 3A statistics * update, as if this frame is never captured. This mode can be used in the scenario diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index dff6227..9046e81 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -153,10 +153,10 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { Handler handler) throws CameraAccessException { if (request == null) { throw new IllegalArgumentException("request must not be null"); - } else if (request.isReprocess() && !isReprocessible()) { + } else if (request.isReprocess() && !isReprocessable()) { throw new IllegalArgumentException("this capture session cannot handle reprocess " + "requests"); - } else if (request.isReprocess() && request.getReprocessibleSessionId() != mId) { + } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) { throw new IllegalArgumentException("capture request was created for another session"); } @@ -184,10 +184,10 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { for (CaptureRequest request : requests) { if (request.isReprocess()) { - if (!isReprocessible()) { + if (!isReprocessable()) { throw new IllegalArgumentException("This capture session cannot handle " + "reprocess requests"); - } else if (request.getReprocessibleSessionId() != mId) { + } else if (request.getReprocessableSessionId() != mId) { throw new IllegalArgumentException("Capture request was created for another " + "session"); } @@ -293,7 +293,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { } @Override - public boolean isReprocessible() { + public boolean isReprocessable() { return mInput != null; } @@ -371,7 +371,6 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { mDeviceImpl.stopRepeating(); } catch (IllegalStateException e) { // OK: Camera device may already be closed, nothing else to do - Log.w(TAG, mIdString + "The camera device was already closed: ", e); // TODO: Fire onClosed anytime we get the device onClosed or the ISE? // or just suppress the ISE only and rely onClosed. diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index e84b46a..4508dc8 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -480,16 +480,16 @@ public class CameraDeviceImpl extends CameraDevice { } @Override - public void createReprocessibleCaptureSession(InputConfiguration inputConfig, + public void createReprocessableCaptureSession(InputConfiguration inputConfig, List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler) throws CameraAccessException { if (DEBUG) { - Log.d(TAG, "createReprocessibleCaptureSession"); + Log.d(TAG, "createReprocessableCaptureSession"); } if (inputConfig == null) { throw new IllegalArgumentException("inputConfig cannot be null when creating a " + - "reprocessible capture session"); + "reprocessable capture session"); } List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size()); for (Surface surface : outputs) { diff --git a/core/java/android/hardware/camera2/legacy/ParameterUtils.java b/core/java/android/hardware/camera2/legacy/ParameterUtils.java index 9e9a6fe..32bbc51 100644 --- a/core/java/android/hardware/camera2/legacy/ParameterUtils.java +++ b/core/java/android/hardware/camera2/legacy/ParameterUtils.java @@ -61,6 +61,8 @@ public class ParameterUtils { public static final Rect RECTANGLE_EMPTY = new Rect(/*left*/0, /*top*/0, /*right*/0, /*bottom*/0); + private static final double ASPECT_RATIO_TOLERANCE = 0.05f; + /** * Calculate effective/reported zoom data from a user-specified crop region. */ @@ -498,7 +500,10 @@ public class ParameterUtils { float aspectRatioPreview = previewSize.getWidth() * 1.0f / previewSize.getHeight(); float cropH, cropW; - if (aspectRatioPreview < aspectRatioArray) { + if (Math.abs(aspectRatioPreview - aspectRatioArray) < ASPECT_RATIO_TOLERANCE) { + cropH = activeArray.height(); + cropW = activeArray.width(); + } else if (aspectRatioPreview < aspectRatioArray) { // The new width must be smaller than the height, so scale the width by AR cropH = activeArray.height(); cropW = cropH * aspectRatioPreview; diff --git a/core/java/android/hardware/camera2/params/InputConfiguration.java b/core/java/android/hardware/camera2/params/InputConfiguration.java index dea1c5c..0c642cf 100644 --- a/core/java/android/hardware/camera2/params/InputConfiguration.java +++ b/core/java/android/hardware/camera2/params/InputConfiguration.java @@ -19,11 +19,11 @@ package android.hardware.camera2.params; import android.hardware.camera2.utils.HashCodeHelpers; /** - * Immutable class to store an input configuration that is used to create a reprocessible capture + * Immutable class to store an input configuration that is used to create a reprocessable capture * session. * - * @see CameraDevice#createReprocessibleCaptureSession - * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP + * @see android.hardware.camera2.CameraDevice#createReprocessableCaptureSession + * @see android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP */ public final class InputConfiguration { diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 02793f1..aa697ea 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -125,11 +125,13 @@ public abstract class DisplayManagerInternal { * mirroring. * @param requestedRefreshRate The preferred refresh rate for the top-most visible window that * has a preference. + * @param requestedModeId The preferred mode id for the top-most visible window that has a + * preference. * @param inTraversal True if called from WindowManagerService during a window traversal * prior to call to performTraversalInTransactionFromWindowManager. */ public abstract void setDisplayProperties(int displayId, boolean hasContent, - float requestedRefreshRate, boolean inTraversal); + float requestedRefreshRate, int requestedModeId, boolean inTraversal); /** * Applies an offset to the contents of a display, for example to avoid burn-in. diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index cf96145..338bd76 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -32,7 +32,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback; -import android.security.AndroidKeyStoreProvider; +import android.security.keystore.AndroidKeyStoreProvider; import android.util.Log; import android.util.Slog; @@ -686,7 +686,7 @@ public class FingerprintManager { private void sendAuthenticatedResult(Fingerprint fp) { if (mAuthenticationCallback != null) { - if (fp.getFingerId() == 0 && fp.getGroupId() == 0) { + if (fp.getFingerId() == 0) { // Fingerprint template valid but doesn't match one in database mAuthenticationCallback.onAuthenticationFailed(); } else { diff --git a/core/java/android/hardware/fingerprint/FingerprintUtils.java b/core/java/android/hardware/fingerprint/FingerprintUtils.java deleted file mode 100644 index 525f254..0000000 --- a/core/java/android/hardware/fingerprint/FingerprintUtils.java +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.hardware.fingerprint; - -import android.content.ContentResolver; -import android.content.Context; -import android.os.Vibrator; -import android.provider.Settings; -import android.text.TextUtils; -import android.util.Log; - -import com.android.internal.util.ArrayUtils; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Utility class for dealing with fingerprints and fingerprint settings. - * @hide - */ -public -class FingerprintUtils { - private static final boolean DEBUG = true; - private static final String TAG = "FingerprintUtils"; - private static final long[] FP_ERROR_VIBRATE_PATTERN = new long[] {0, 30, 100, 30}; - private static final long[] FP_SUCCESS_VIBRATE_PATTERN = new long[] {0, 30}; - - private static int[] toIntArray(List<Integer> list) { - if (list == null) { - return null; - } - int[] arr = new int[list.size()]; - int i = 0; - for (int elem : list) { - arr[i] = elem; - i++; - } - return arr; - } - - public static int[] getFingerprintIdsForUser(ContentResolver res, int userId) { - String fingerIdsRaw = Settings.Secure.getStringForUser(res, - Settings.Secure.USER_FINGERPRINT_IDS, userId); - ArrayList<Integer> tmp = new ArrayList<Integer>(); - if (!TextUtils.isEmpty(fingerIdsRaw)) { - String[] fingerStringIds = fingerIdsRaw.replace("[","").replace("]","").split(", "); - int length = fingerStringIds.length; - for (int i = 0; i < length; i++) { - try { - tmp.add(Integer.decode(fingerStringIds[i])); - } catch (NumberFormatException e) { - if (DEBUG) Log.w(TAG, "Error parsing finger id: '" + fingerStringIds[i] + "'"); - } - } - } - return toIntArray(tmp); - } - - public static void addFingerprintIdForUser(ContentResolver res, int fingerId, int userId) { - // FingerId 0 has special meaning. - if (fingerId == 0) { - Log.w(TAG, "Tried to add fingerId 0"); - return; - } - - int[] fingerIds = getFingerprintIdsForUser(res, userId); - - // Don't allow dups - if (ArrayUtils.contains(fingerIds, fingerId)) { - Log.w(TAG, "finger already added " + fingerId); - return; - } - int[] newList = Arrays.copyOf(fingerIds, fingerIds.length + 1); - newList[fingerIds.length] = fingerId; - Settings.Secure.putStringForUser(res, Settings.Secure.USER_FINGERPRINT_IDS, - Arrays.toString(newList), userId); - } - - public static boolean removeFingerprintIdForUser(int fingerId, ContentResolver res, int userId) - { - // FingerId 0 has special meaning. The HAL layer is supposed to remove each finger one - // at a time and invoke notify() for each fingerId. If we get called with 0 here, it means - // something bad has happened. - if (fingerId == 0) throw new IllegalArgumentException("fingerId can't be 0"); - - final int[] fingerIds = getFingerprintIdsForUser(res, userId); - if (ArrayUtils.contains(fingerIds, fingerId)) { - final int[] result = ArrayUtils.removeInt(fingerIds, fingerId); - Settings.Secure.putStringForUser(res, Settings.Secure.USER_FINGERPRINT_IDS, - Arrays.toString(result), userId); - return true; - } - return false; - } - - public static void vibrateFingerprintError(Context context) { - Vibrator vibrator = context.getSystemService(Vibrator.class); - if (vibrator != null) { - vibrator.vibrate(FP_ERROR_VIBRATE_PATTERN, -1); - } - } - - public static void vibrateFingerprintSuccess(Context context) { - Vibrator vibrator = context.getSystemService(Vibrator.class); - if (vibrator != null) { - vibrator.vibrate(FP_SUCCESS_VIBRATE_PATTERN, -1); - } - } - -}; - diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index e4ab3f2..881dc0f 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -83,7 +83,7 @@ interface IUsbManager void clearDefaults(String packageName, int userId); /* Sets the current USB function. */ - void setCurrentFunction(String function, boolean makeDefault); + void setCurrentFunction(String function); /* Allow USB debugging from the attached host. If alwaysAllow is true, add the * the public key to list of host keys that the user has approved. diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index a45d4ca..000d41f 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -228,6 +228,23 @@ public class UsbManager { */ public static final String EXTRA_PERMISSION_GRANTED = "permission"; + /** + * The persistent property which stores whether adb is enabled or not. Other values are ignored. + * Previously this value stored non-adb settings, but not anymore. + * TODO: rename this to something adb specific, rather than using usb. + * + * {@hide} + */ + public static final String ADB_PERSISTENT_PROPERTY = "persist.sys.usb.config"; + + /** + * The non-persistent property which stores the current USB settings. + * + * {@hide} + */ + public static final String USB_SETTINGS_PROPERTY = "sys.usb.config"; + + private final Context mContext; private final IUsbManager mService; @@ -410,21 +427,26 @@ public class UsbManager { } } + private static boolean propertyContainsFunction(String property, String function) { + String functions = SystemProperties.get(property, ""); + int index = functions.indexOf(function); + if (index < 0) return false; + if (index > 0 && functions.charAt(index - 1) != ',') return false; + int charAfter = index + function.length(); + if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false; + return true; + } + /** - * Returns the current default USB function. + * Returns true if the specified USB function is currently enabled. * - * @return name of the default function. + * @param function name of the USB function + * @return true if the USB function is enabled. * * {@hide} */ - public String getDefaultFunction() { - String functions = SystemProperties.get("persist.sys.usb.config", ""); - int commaIndex = functions.indexOf(','); - if (commaIndex > 0) { - return functions.substring(0, commaIndex); - } else { - return functions; - } + public boolean isFunctionEnabled(String function) { + return propertyContainsFunction(USB_SETTINGS_PROPERTY, function); } /** @@ -432,13 +454,12 @@ public class UsbManager { * If function is null, then the current function is set to the default function. * * @param function name of the USB function, or null to restore the default function - * @param makeDefault true if the function should be set as the new default function * * {@hide} */ - public void setCurrentFunction(String function, boolean makeDefault) { + public void setCurrentFunction(String function) { try { - mService.setCurrentFunction(function, makeDefault); + mService.setCurrentFunction(function); } catch (RemoteException e) { Log.e(TAG, "RemoteException in setCurrentFunction", e); } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index d8c3361..26878c0 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -496,6 +496,8 @@ public class ConnectivityManager { * Tests if a given integer represents a valid network type. * @param networkType the type to be tested * @return a boolean. {@code true} if the type is valid, else {@code false} + * @deprecated All APIs accepting a network type are deprecated. There should be no need to + * validate a network type. */ public static boolean isNetworkTypeValid(int networkType) { return networkType >= 0 && networkType <= MAX_NETWORK_TYPE; diff --git a/core/java/android/net/IpReachabilityMonitor.java b/core/java/android/net/IpReachabilityMonitor.java new file mode 100644 index 0000000..7e1c05b --- /dev/null +++ b/core/java/android/net/IpReachabilityMonitor.java @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.ProxyInfo; +import android.net.RouteInfo; +import android.net.netlink.NetlinkConstants; +import android.net.netlink.NetlinkErrorMessage; +import android.net.netlink.NetlinkMessage; +import android.net.netlink.NetlinkSocket; +import android.net.netlink.RtNetlinkNeighborMessage; +import android.net.netlink.StructNdaCacheInfo; +import android.net.netlink.StructNdMsg; +import android.net.netlink.StructNlMsgHdr; +import android.os.SystemClock; +import android.system.ErrnoException; +import android.system.NetlinkSocketAddress; +import android.system.OsConstants; +import android.text.TextUtils; +import android.util.Log; + +import java.io.InterruptedIOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + + +/** + * IpReachabilityMonitor. + * + * Monitors on-link IP reachability and notifies callers whenever any on-link + * addresses of interest appear to have become unresponsive. + * + * @hide + */ +public class IpReachabilityMonitor { + private static final String TAG = "IpReachabilityMonitor"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + public interface Callback { + public void notifyLost(InetAddress ip, String logMsg); + } + + private final Object mLock = new Object(); + private final String mInterfaceName; + private final int mInterfaceIndex; + private final Callback mCallback; + private final Set<InetAddress> mIpWatchList; + private int mIpWatchListVersion; + private boolean mRunning; + final private Thread mObserverThread; + + // TODO: consider passing in a NetworkInterface object from the caller. + public IpReachabilityMonitor(String ifName, Callback callback) throws IllegalArgumentException { + mInterfaceName = ifName; + int ifIndex = -1; + try { + NetworkInterface netIf = NetworkInterface.getByName(ifName); + mInterfaceIndex = netIf.getIndex(); + } catch (SocketException | NullPointerException e) { + throw new IllegalArgumentException("invalid interface '" + ifName + "': ", e); + } + mCallback = callback; + mIpWatchList = new HashSet<InetAddress>(); + mIpWatchListVersion = 0; + mRunning = false; + mObserverThread = new Thread(new NetlinkSocketObserver()); + mObserverThread.start(); + } + + public void stop() { + synchronized (mLock) { + mRunning = false; + mIpWatchList.clear(); + } + } + + // TODO: add a public dump() method that can be called during a bug report. + + private static Set<InetAddress> getOnLinkNeighbors(LinkProperties lp) { + Set<InetAddress> allIps = new HashSet<InetAddress>(); + + final List<RouteInfo> routes = lp.getRoutes(); + for (RouteInfo route : routes) { + if (route.hasGateway()) { + allIps.add(route.getGateway()); + } + } + + for (InetAddress nameserver : lp.getDnsServers()) { + allIps.add(nameserver); + } + + try { + // Don't block here for DNS lookups. If the proxy happens to be an + // IP literal then we add it the list, but otherwise skip it. + allIps.add(NetworkUtils.numericToInetAddress(lp.getHttpProxy().getHost())); + } catch (NullPointerException|IllegalArgumentException e) { + // No proxy, PAC proxy, or proxy is not a literal IP address. + } + + Set<InetAddress> neighbors = new HashSet<InetAddress>(); + for (InetAddress ip : allIps) { + // TODO: consider using the prefixes of the LinkAddresses instead + // of the routes--it may be more accurate. + for (RouteInfo route : routes) { + if (route.hasGateway()) { + continue; // Not directly connected. + } + if (route.matches(ip)) { + neighbors.add(ip); + break; + } + } + } + return neighbors; + } + + private String describeWatchList() { + synchronized (mLock) { + return "version{" + mIpWatchListVersion + "}, " + + "ips=[" + TextUtils.join(",", mIpWatchList) + "]"; + } + } + + private boolean isWatching(InetAddress ip) { + synchronized (mLock) { + return mRunning && mIpWatchList.contains(ip); + } + } + + public void updateLinkProperties(LinkProperties lp) { + if (!mInterfaceName.equals(lp.getInterfaceName())) { + // TODO: figure out how to cope with interface changes. + Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() + + "' does not match: " + mInterfaceName); + return; + } + + // We rely upon the caller to determine when LinkProperties have actually + // changed and call this at the appropriate time. Note that even though + // the LinkProperties may change, the set of on-link neighbors might not. + // + // Nevertheless, just clear and re-add everything. + final Set<InetAddress> neighbors = getOnLinkNeighbors(lp); + if (neighbors.isEmpty()) { + return; + } + + synchronized (mLock) { + mIpWatchList.clear(); + mIpWatchList.addAll(neighbors); + mIpWatchListVersion++; + } + if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); } + } + + public void clearLinkProperties() { + synchronized (mLock) { + mIpWatchList.clear(); + mIpWatchListVersion++; + } + if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); } + } + + private void notifyLost(InetAddress ip, String msg) { + if (!isWatching(ip)) { + // Ignore stray notifications. This can happen when, for example, + // several neighbors are reported unreachable or deleted + // back-to-back. Because these messages are parsed serially, and + // this method is called for each notification, the caller above us + // may have already processed an earlier lost notification and + // cleared the watch list as it moves to handle the situation. + return; + } + Log.w(TAG, "ALERT: " + ip.getHostAddress() + " -- " + msg); + if (mCallback != null) { + mCallback.notifyLost(ip, msg); + } + } + + + private final class NetlinkSocketObserver implements Runnable { + private static final String TAG = "NetlinkSocketObserver"; + private NetlinkSocket mSocket; + + @Override + public void run() { + if (VDBG) { Log.d(TAG, "Starting observing thread."); } + synchronized (mLock) { mRunning = true; } + + try { + setupNetlinkSocket(); + } catch (ErrnoException | SocketException e) { + Log.e(TAG, "Failed to suitably initialize a netlink socket", e); + synchronized (mLock) { mRunning = false; } + } + + ByteBuffer byteBuffer; + while (stillRunning()) { + try { + byteBuffer = recvKernelReply(); + } catch (ErrnoException e) { + Log.w(TAG, "ErrnoException: ", e); + break; + } + final long whenMs = SystemClock.elapsedRealtime(); + if (byteBuffer == null) { + continue; + } + parseNetlinkMessageBuffer(byteBuffer, whenMs); + } + + clearNetlinkSocket(); + + synchronized (mLock) { mRunning = false; } + if (VDBG) { Log.d(TAG, "Finishing observing thread."); } + } + + private boolean stillRunning() { + synchronized (mLock) { + return mRunning; + } + } + + private void clearNetlinkSocket() { + if (mSocket != null) { + mSocket.close(); + } + mSocket = null; + } + + // TODO: Refactor the main loop to recreate the socket upon recoverable errors. + private void setupNetlinkSocket() throws ErrnoException, SocketException { + clearNetlinkSocket(); + mSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE); + + final NetlinkSocketAddress listenAddr = new NetlinkSocketAddress( + 0, OsConstants.RTMGRP_NEIGH); + mSocket.bind(listenAddr); + + if (VDBG) { + final NetlinkSocketAddress nlAddr = mSocket.getLocalAddress(); + Log.d(TAG, "bound to sockaddr_nl{" + + ((long) (nlAddr.getPortId() & 0xffffffff)) + ", " + + nlAddr.getGroupsMask() + + "}"); + } + } + + private ByteBuffer recvKernelReply() throws ErrnoException { + try { + return mSocket.recvMessage(0); + } catch (InterruptedIOException e) { + // Interruption or other error, e.g. another thread closed our file descriptor. + } catch (ErrnoException e) { + if (e.errno != OsConstants.EAGAIN) { + throw e; + } + } + return null; + } + + private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) { + while (byteBuffer.remaining() > 0) { + final int position = byteBuffer.position(); + final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer); + if (nlMsg == null || nlMsg.getHeader() == null) { + byteBuffer.position(position); + Log.e(TAG, "unparsable netlink msg: " + NetlinkConstants.hexify(byteBuffer)); + break; + } + + final int srcPortId = nlMsg.getHeader().nlmsg_pid; + if (srcPortId != 0) { + Log.e(TAG, "non-kernel source portId: " + ((long) (srcPortId & 0xffffffff))); + break; + } + + if (nlMsg instanceof NetlinkErrorMessage) { + Log.e(TAG, "netlink error: " + nlMsg); + continue; + } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) { + if (DBG) { + Log.d(TAG, "non-rtnetlink neighbor msg: " + nlMsg); + } + continue; + } + + evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs); + } + } + + private void evaluateRtNetlinkNeighborMessage( + RtNetlinkNeighborMessage neighMsg, long whenMs) { + final StructNdMsg ndMsg = neighMsg.getNdHeader(); + if (ndMsg == null || ndMsg.ndm_ifindex != mInterfaceIndex) { + return; + } + + final InetAddress destination = neighMsg.getDestination(); + if (!isWatching(destination)) { + return; + } + + final short msgType = neighMsg.getHeader().nlmsg_type; + final short nudState = ndMsg.ndm_state; + final String eventMsg = "NeighborEvent{" + + "elapsedMs=" + whenMs + ", " + + destination.getHostAddress() + ", " + + "[" + NetlinkConstants.hexify(neighMsg.getLinkLayerAddress()) + "], " + + NetlinkConstants.stringForNlMsgType(msgType) + ", " + + StructNdMsg.stringForNudState(nudState) + + "}"; + + if (VDBG) { + Log.d(TAG, neighMsg.toString()); + } else if (DBG) { + Log.d(TAG, eventMsg); + } + + if ((msgType == NetlinkConstants.RTM_DELNEIGH) || + (nudState == StructNdMsg.NUD_FAILED)) { + final String logMsg = "FAILURE: " + eventMsg; + notifyLost(destination, logMsg); + } + } + } +} diff --git a/core/java/android/net/netlink/NetlinkConstants.java b/core/java/android/net/netlink/NetlinkConstants.java new file mode 100644 index 0000000..e331701 --- /dev/null +++ b/core/java/android/net/netlink/NetlinkConstants.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.system.OsConstants; +import com.android.internal.util.HexDump; + +import java.nio.ByteBuffer; + + +/** + * Various constants and static helper methods for netlink communications. + * + * Values taken from: + * + * <linux_src>/include/uapi/linux/netlink.h + * <linux_src>/include/uapi/linux/rtnetlink.h + * + * @hide + */ +public class NetlinkConstants { + private NetlinkConstants() {} + + public static final int NLA_ALIGNTO = 4; + + public static final int alignedLengthOf(short length) { + final int intLength = (int) length & 0xffff; + return alignedLengthOf(intLength); + } + + public static final int alignedLengthOf(int length) { + if (length <= 0) { return 0; } + return (((length + NLA_ALIGNTO - 1) / NLA_ALIGNTO) * NLA_ALIGNTO); + } + + public static String stringForAddressFamily(int family) { + if (family == OsConstants.AF_INET) { return "AF_INET"; } + if (family == OsConstants.AF_INET6) { return "AF_INET6"; } + if (family == OsConstants.AF_NETLINK) { return "AF_NETLINK"; } + return String.valueOf(family); + } + + public static String hexify(byte[] bytes) { + if (bytes == null) { return "(null)"; } + return HexDump.toHexString(bytes); + } + + public static String hexify(ByteBuffer buffer) { + if (buffer == null) { return "(null)"; } + return HexDump.toHexString( + buffer.array(), buffer.position(), buffer.remaining()); + } + + // Known values for struct nlmsghdr nlm_type. + public static final short NLMSG_NOOP = 1; // Nothing + public static final short NLMSG_ERROR = 2; // Error + public static final short NLMSG_DONE = 3; // End of a dump + public static final short NLMSG_OVERRUN = 4; // Data lost + public static final short NLMSG_MAX_RESERVED = 15; // Max reserved value + + public static final short RTM_NEWLINK = 16; + public static final short RTM_DELLINK = 17; + public static final short RTM_GETLINK = 18; + public static final short RTM_SETLINK = 19; + public static final short RTM_NEWADDR = 20; + public static final short RTM_DELADDR = 21; + public static final short RTM_GETADDR = 22; + public static final short RTM_NEWROUTE = 24; + public static final short RTM_DELROUTE = 25; + public static final short RTM_GETROUTE = 26; + public static final short RTM_NEWNEIGH = 28; + public static final short RTM_DELNEIGH = 29; + public static final short RTM_GETNEIGH = 30; + public static final short RTM_NEWRULE = 32; + public static final short RTM_DELRULE = 33; + public static final short RTM_GETRULE = 34; + public static final short RTM_NEWNDUSEROPT = 68; + + public static String stringForNlMsgType(short nlm_type) { + switch (nlm_type) { + case NLMSG_NOOP: return "NLMSG_NOOP"; + case NLMSG_ERROR: return "NLMSG_ERROR"; + case NLMSG_DONE: return "NLMSG_DONE"; + case NLMSG_OVERRUN: return "NLMSG_OVERRUN"; + case RTM_NEWLINK: return "RTM_NEWLINK"; + case RTM_DELLINK: return "RTM_DELLINK"; + case RTM_GETLINK: return "RTM_GETLINK"; + case RTM_SETLINK: return "RTM_SETLINK"; + case RTM_NEWADDR: return "RTM_NEWADDR"; + case RTM_DELADDR: return "RTM_DELADDR"; + case RTM_GETADDR: return "RTM_GETADDR"; + case RTM_NEWROUTE: return "RTM_NEWROUTE"; + case RTM_DELROUTE: return "RTM_DELROUTE"; + case RTM_GETROUTE: return "RTM_GETROUTE"; + case RTM_NEWNEIGH: return "RTM_NEWNEIGH"; + case RTM_DELNEIGH: return "RTM_DELNEIGH"; + case RTM_GETNEIGH: return "RTM_GETNEIGH"; + case RTM_NEWRULE: return "RTM_NEWRULE"; + case RTM_DELRULE: return "RTM_DELRULE"; + case RTM_GETRULE: return "RTM_GETRULE"; + case RTM_NEWNDUSEROPT: return "RTM_NEWNDUSEROPT"; + default: + return "unknown RTM type: " + String.valueOf(nlm_type); + } + } +} diff --git a/core/java/android/net/netlink/NetlinkErrorMessage.java b/core/java/android/net/netlink/NetlinkErrorMessage.java new file mode 100644 index 0000000..dbc10d6 --- /dev/null +++ b/core/java/android/net/netlink/NetlinkErrorMessage.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.net.netlink.StructNlMsgHdr; +import android.net.netlink.NetlinkMessage; +import android.util.Log; + +import java.nio.ByteBuffer; + + +/** + * A NetlinkMessage subclass for netlink error messages. + * + * @hide + */ +public class NetlinkErrorMessage extends NetlinkMessage { + + public static NetlinkErrorMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) { + final NetlinkErrorMessage errorMsg = new NetlinkErrorMessage(header); + + errorMsg.mNlMsgErr = StructNlMsgErr.parse(byteBuffer); + if (errorMsg.mNlMsgErr == null) { + return null; + } + + return errorMsg; + } + + private StructNlMsgErr mNlMsgErr; + + NetlinkErrorMessage(StructNlMsgHdr header) { + super(header); + mNlMsgErr = null; + } + + public StructNlMsgErr getNlMsgError() { + return mNlMsgErr; + } + + @Override + public String toString() { + return "NetlinkErrorMessage{ " + + "nlmsghdr{" + (mHeader == null ? "" : mHeader.toString()) + "}, " + + "nlmsgerr{" + (mNlMsgErr == null ? "" : mNlMsgErr.toString()) + "} " + + "}"; + } +} diff --git a/core/java/android/net/netlink/NetlinkMessage.java b/core/java/android/net/netlink/NetlinkMessage.java new file mode 100644 index 0000000..bc04a16 --- /dev/null +++ b/core/java/android/net/netlink/NetlinkMessage.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.net.netlink.NetlinkConstants; +import android.net.netlink.NetlinkErrorMessage; +import android.net.netlink.RtNetlinkNeighborMessage; +import android.net.netlink.StructNlAttr; +import android.net.netlink.StructNlMsgHdr; +import android.util.Log; + +import java.nio.ByteBuffer; + + +/** + * NetlinkMessage base class for other, more specific netlink message types. + * + * Classes that extend NetlinkMessage should: + * - implement a public static parse(StructNlMsgHdr, ByteBuffer) method + * - returning either null (parse errors) or a new object of the subclass + * type (cast-able to NetlinkMessage) + * + * NetlinkMessage.parse() should be updated to know which nlmsg_type values + * correspond with which message subclasses. + * + * @hide + */ +public class NetlinkMessage { + private final static String TAG = "NetlinkMessage"; + + public static NetlinkMessage parse(ByteBuffer byteBuffer) { + final int startPosition = (byteBuffer != null) ? byteBuffer.position() : -1; + final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(byteBuffer); + if (nlmsghdr == null) { + return null; + } + + int payloadLength = NetlinkConstants.alignedLengthOf(nlmsghdr.nlmsg_len); + payloadLength -= StructNlMsgHdr.STRUCT_SIZE; + if (payloadLength < 0 || payloadLength > byteBuffer.remaining()) { + // Malformed message or runt buffer. Pretend the buffer was consumed. + byteBuffer.position(byteBuffer.limit()); + return null; + } + + switch (nlmsghdr.nlmsg_type) { + //case NetlinkConstants.NLMSG_NOOP: + case NetlinkConstants.NLMSG_ERROR: + return (NetlinkMessage) NetlinkErrorMessage.parse(byteBuffer); + case NetlinkConstants.NLMSG_DONE: + byteBuffer.position(byteBuffer.position() + payloadLength); + return new NetlinkMessage(nlmsghdr); + //case NetlinkConstants.NLMSG_OVERRUN: + case NetlinkConstants.RTM_NEWNEIGH: + case NetlinkConstants.RTM_DELNEIGH: + case NetlinkConstants.RTM_GETNEIGH: + return (NetlinkMessage) RtNetlinkNeighborMessage.parse(nlmsghdr, byteBuffer); + default: + if (nlmsghdr.nlmsg_type <= NetlinkConstants.NLMSG_MAX_RESERVED) { + // Netlink control message. Just parse the header for now, + // pretending the whole message was consumed. + byteBuffer.position(byteBuffer.position() + payloadLength); + return new NetlinkMessage(nlmsghdr); + } + return null; + } + } + + protected StructNlMsgHdr mHeader; + + public NetlinkMessage(StructNlMsgHdr nlmsghdr) { + mHeader = nlmsghdr; + } + + public StructNlMsgHdr getHeader() { + return mHeader; + } + + @Override + public String toString() { + return "NetlinkMessage{" + (mHeader == null ? "" : mHeader.toString()) + "}"; + } +} diff --git a/core/java/android/net/netlink/NetlinkSocket.java b/core/java/android/net/netlink/NetlinkSocket.java new file mode 100644 index 0000000..657d48c --- /dev/null +++ b/core/java/android/net/netlink/NetlinkSocket.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.system.ErrnoException; +import android.system.NetlinkSocketAddress; +import android.system.Os; +import android.system.OsConstants; +import android.system.StructTimeval; +import android.util.Log; +import libcore.io.IoUtils; +import libcore.io.Libcore; + +import java.io.Closeable; +import java.io.FileDescriptor; +import java.io.InterruptedIOException; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + + +/** + * NetlinkSocket + * + * A small wrapper class to assist with AF_NETLINK socket operations. + * + * @hide + */ +public class NetlinkSocket implements Closeable { + private static final String TAG = "NetlinkSocket"; + private static final int SOCKET_RECV_BUFSIZE = 64 * 1024; + private static final int DEFAULT_RECV_BUFSIZE = 8 * 1024; + + final private FileDescriptor mDescriptor; + private NetlinkSocketAddress mAddr; + private long mLastRecvTimeoutMs; + private long mLastSendTimeoutMs; + + public NetlinkSocket(int nlProto) throws ErrnoException { + mDescriptor = Os.socket( + OsConstants.AF_NETLINK, OsConstants.SOCK_DGRAM, nlProto); + + Libcore.os.setsockoptInt( + mDescriptor, OsConstants.SOL_SOCKET, + OsConstants.SO_RCVBUF, SOCKET_RECV_BUFSIZE); + } + + public NetlinkSocketAddress getLocalAddress() throws ErrnoException { + return (NetlinkSocketAddress) Os.getsockname(mDescriptor); + } + + public void bind(NetlinkSocketAddress localAddr) throws ErrnoException, SocketException { + Os.bind(mDescriptor, (SocketAddress)localAddr); + } + + public void connectTo(NetlinkSocketAddress peerAddr) + throws ErrnoException, SocketException { + Os.connect(mDescriptor, (SocketAddress) peerAddr); + } + + public void connectToKernel() throws ErrnoException, SocketException { + connectTo(new NetlinkSocketAddress(0, 0)); + } + + /** + * Wait indefinitely (or until underlying socket error) for a + * netlink message of at most DEFAULT_RECV_BUFSIZE size. + */ + public ByteBuffer recvMessage() + throws ErrnoException, InterruptedIOException { + return recvMessage(DEFAULT_RECV_BUFSIZE, 0); + } + + /** + * Wait up to |timeoutMs| (or until underlying socket error) for a + * netlink message of at most DEFAULT_RECV_BUFSIZE size. + */ + public ByteBuffer recvMessage(long timeoutMs) throws ErrnoException, InterruptedIOException { + return recvMessage(DEFAULT_RECV_BUFSIZE, timeoutMs); + } + + private void checkTimeout(long timeoutMs) { + if (timeoutMs < 0) { + throw new IllegalArgumentException("Negative timeouts not permitted"); + } + } + + /** + * Wait up to |timeoutMs| (or until underlying socket error) for a + * netlink message of at most |bufsize| size. + * + * Multi-threaded calls with different timeouts will cause unexpected results. + */ + public ByteBuffer recvMessage(int bufsize, long timeoutMs) + throws ErrnoException, IllegalArgumentException, InterruptedIOException { + checkTimeout(timeoutMs); + + synchronized (mDescriptor) { + if (mLastRecvTimeoutMs != timeoutMs) { + Os.setsockoptTimeval(mDescriptor, + OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO, + StructTimeval.fromMillis(timeoutMs)); + mLastRecvTimeoutMs = timeoutMs; + } + } + + ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize); + int length = Os.read(mDescriptor, byteBuffer); + if (length == bufsize) { + Log.w(TAG, "maximum read"); + } + byteBuffer.position(0); + byteBuffer.limit(length); + byteBuffer.order(ByteOrder.nativeOrder()); + return byteBuffer; + } + + /** + * Send a message to a peer to which this socket has previously connected. + * + * This blocks until completion or an error occurs. + */ + public boolean sendMessage(byte[] bytes, int offset, int count) + throws ErrnoException, InterruptedIOException { + return sendMessage(bytes, offset, count, 0); + } + + /** + * Send a message to a peer to which this socket has previously connected, + * waiting at most |timeoutMs| milliseconds for the send to complete. + * + * Multi-threaded calls with different timeouts will cause unexpected results. + */ + public boolean sendMessage(byte[] bytes, int offset, int count, long timeoutMs) + throws ErrnoException, IllegalArgumentException, InterruptedIOException { + checkTimeout(timeoutMs); + + synchronized (mDescriptor) { + if (mLastSendTimeoutMs != timeoutMs) { + Os.setsockoptTimeval(mDescriptor, + OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO, + StructTimeval.fromMillis(timeoutMs)); + mLastSendTimeoutMs = timeoutMs; + } + } + + return (count == Os.write(mDescriptor, bytes, offset, count)); + } + + @Override + public void close() { + IoUtils.closeQuietly(mDescriptor); + } +} diff --git a/core/java/android/net/netlink/RtNetlinkNeighborMessage.java b/core/java/android/net/netlink/RtNetlinkNeighborMessage.java new file mode 100644 index 0000000..d4b572c --- /dev/null +++ b/core/java/android/net/netlink/RtNetlinkNeighborMessage.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.net.netlink.StructNdaCacheInfo; +import android.net.netlink.StructNdMsg; +import android.net.netlink.StructNlAttr; +import android.net.netlink.StructNlMsgHdr; +import android.net.netlink.NetlinkMessage; +import android.util.Log; + +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + + +/** + * A NetlinkMessage subclass for netlink error messages. + * + * see also: <linux_src>/include/uapi/linux/neighbour.h + * + * @hide + */ +public class RtNetlinkNeighborMessage extends NetlinkMessage { + public static final short NDA_UNSPEC = 0; + public static final short NDA_DST = 1; + public static final short NDA_LLADDR = 2; + public static final short NDA_CACHEINFO = 3; + public static final short NDA_PROBES = 4; + public static final short NDA_VLAN = 5; + public static final short NDA_PORT = 6; + public static final short NDA_VNI = 7; + public static final short NDA_IFINDEX = 8; + public static final short NDA_MASTER = 9; + + private static StructNlAttr findNextAttrOfType(short attrType, ByteBuffer byteBuffer) { + while (byteBuffer != null && byteBuffer.remaining() > 0) { + final StructNlAttr nlAttr = StructNlAttr.peek(byteBuffer); + if (nlAttr == null) { + break; + } + if (nlAttr.nla_type == attrType) { + return StructNlAttr.parse(byteBuffer); + } + if (byteBuffer.remaining() < nlAttr.getAlignedLength()) { + break; + } + byteBuffer.position(byteBuffer.position() + nlAttr.getAlignedLength()); + } + return null; + } + + public static RtNetlinkNeighborMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) { + final RtNetlinkNeighborMessage neighMsg = new RtNetlinkNeighborMessage(header); + + neighMsg.mNdmsg = StructNdMsg.parse(byteBuffer); + if (neighMsg.mNdmsg == null) { + return null; + } + + // Some of these are message-type dependent, and not always present. + final int baseOffset = byteBuffer.position(); + StructNlAttr nlAttr = findNextAttrOfType(NDA_DST, byteBuffer); + if (nlAttr != null) { + neighMsg.mDestination = nlAttr.getValueAsInetAddress(); + } + + byteBuffer.position(baseOffset); + nlAttr = findNextAttrOfType(NDA_LLADDR, byteBuffer); + if (nlAttr != null) { + neighMsg.mLinkLayerAddr = nlAttr.nla_value; + } + + byteBuffer.position(baseOffset); + nlAttr = findNextAttrOfType(NDA_PROBES, byteBuffer); + if (nlAttr != null) { + neighMsg.mNumProbes = nlAttr.getValueAsInt(0); + } + + byteBuffer.position(baseOffset); + nlAttr = findNextAttrOfType(NDA_CACHEINFO, byteBuffer); + if (nlAttr != null) { + neighMsg.mCacheInfo = StructNdaCacheInfo.parse(nlAttr.getValueAsByteBuffer()); + } + + final int kMinConsumed = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE; + final int kAdditionalSpace = NetlinkConstants.alignedLengthOf( + neighMsg.mHeader.nlmsg_len - kMinConsumed); + if (byteBuffer.remaining() < kAdditionalSpace) { + byteBuffer.position(byteBuffer.limit()); + } else { + byteBuffer.position(baseOffset + kAdditionalSpace); + } + + return neighMsg; + } + + /** + * A convenience method to create an RTM_GETNEIGH request message. + */ + public static byte[] newGetNeighborsRequest(int seqNo) { + final int length = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE; + final byte[] bytes = new byte[length]; + final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); + byteBuffer.order(ByteOrder.nativeOrder()); + + final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr(); + nlmsghdr.nlmsg_len = length; + nlmsghdr.nlmsg_type = NetlinkConstants.RTM_GETNEIGH; + nlmsghdr.nlmsg_flags = StructNlMsgHdr.NLM_F_REQUEST|StructNlMsgHdr.NLM_F_DUMP; + nlmsghdr.nlmsg_seq = seqNo; + nlmsghdr.pack(byteBuffer); + + final StructNdMsg ndmsg = new StructNdMsg(); + ndmsg.pack(byteBuffer); + + return bytes; + } + + private StructNdMsg mNdmsg; + private InetAddress mDestination; + private byte[] mLinkLayerAddr; + private int mNumProbes; + private StructNdaCacheInfo mCacheInfo; + + private RtNetlinkNeighborMessage(StructNlMsgHdr header) { + super(header); + mNdmsg = null; + mDestination = null; + mLinkLayerAddr = null; + mNumProbes = 0; + mCacheInfo = null; + } + + public StructNdMsg getNdHeader() { + return mNdmsg; + } + + public InetAddress getDestination() { + return mDestination; + } + + public byte[] getLinkLayerAddress() { + return mLinkLayerAddr; + } + + public int getProbes() { + return mNumProbes; + } + + public StructNdaCacheInfo getCacheInfo() { + return mCacheInfo; + } + + @Override + public String toString() { + final String ipLiteral = (mDestination == null) ? "" : mDestination.getHostAddress(); + return "RtNetlinkNeighborMessage{ " + + "nlmsghdr{" + (mHeader == null ? "" : mHeader.toString()) + "}, " + + "ndmsg{" + (mNdmsg == null ? "" : mNdmsg.toString()) + "}, " + + "destination{" + ipLiteral + "} " + + "linklayeraddr{" + NetlinkConstants.hexify(mLinkLayerAddr) + "} " + + "probes{" + mNumProbes + "} " + + "cacheinfo{" + (mCacheInfo == null ? "" : mCacheInfo.toString()) + "} " + + "}"; + } +} diff --git a/core/java/android/net/netlink/StructNdMsg.java b/core/java/android/net/netlink/StructNdMsg.java new file mode 100644 index 0000000..e66d45d --- /dev/null +++ b/core/java/android/net/netlink/StructNdMsg.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.net.netlink.NetlinkConstants; +import android.system.OsConstants; +import java.nio.ByteBuffer; + + +/** + * struct ndmsg + * + * see: <linux_src>/include/uapi/linux/neighbour.h + * + * @hide + */ +public class StructNdMsg { + // Already aligned. + public static final int STRUCT_SIZE = 12; + + // Neighbor Cache Entry States + public static final short NUD_INCOMPLETE = 0x01; + public static final short NUD_REACHABLE = 0x02; + public static final short NUD_STALE = 0x04; + public static final short NUD_DELAY = 0x08; + public static final short NUD_PROBE = 0x10; + public static final short NUD_FAILED = 0x20; + public static final short NUD_NOARP = 0x40; + public static final short NUD_PERMANENT = 0x80; + + public static String stringForNudState(short nudState) { + switch (nudState) { + case NUD_INCOMPLETE: return "NUD_INCOMPLETE"; + case NUD_REACHABLE: return "NUD_REACHABLE"; + case NUD_STALE: return "NUD_STALE"; + case NUD_DELAY: return "NUD_DELAY"; + case NUD_PROBE: return "NUD_PROBE"; + case NUD_FAILED: return "NUD_FAILED"; + case NUD_NOARP: return "NUD_NOARP"; + case NUD_PERMANENT: return "NUD_PERMANENT"; + default: + return "unknown NUD state: " + String.valueOf(nudState); + } + } + + public static boolean isNudStateConnected(short nudState) { + return ((nudState & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE)) != 0); + } + + // Neighbor Cache Entry Flags + public static byte NTF_USE = (byte) 0x01; + public static byte NTF_SELF = (byte) 0x02; + public static byte NTF_MASTER = (byte) 0x04; + public static byte NTF_PROXY = (byte) 0x08; + public static byte NTF_ROUTER = (byte) 0x80; + + public static String stringForNudFlags(byte flags) { + final StringBuilder sb = new StringBuilder(); + if ((flags & NTF_USE) != 0) { + sb.append("NTF_USE"); + } + if ((flags & NTF_SELF) != 0) { + if (sb.length() > 0) { sb.append("|"); } + sb.append("NTF_SELF"); + } + if ((flags & NTF_MASTER) != 0) { + if (sb.length() > 0) { sb.append("|"); } + sb.append("NTF_MASTER"); + } + if ((flags & NTF_PROXY) != 0) { + if (sb.length() > 0) { sb.append("|"); + } + sb.append("NTF_PROXY"); } + if ((flags & NTF_ROUTER) != 0) { + if (sb.length() > 0) { sb.append("|"); } + sb.append("NTF_ROUTER"); + } + return sb.toString(); + } + + private static boolean hasAvailableSpace(ByteBuffer byteBuffer) { + return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE; + } + + public static StructNdMsg parse(ByteBuffer byteBuffer) { + if (!hasAvailableSpace(byteBuffer)) { return null; } + + // The ByteOrder must have already been set by the caller. In most + // cases ByteOrder.nativeOrder() is correct, with the possible + // exception of usage within unittests. + final StructNdMsg struct = new StructNdMsg(); + struct.ndm_family = byteBuffer.get(); + final byte pad1 = byteBuffer.get(); + final short pad2 = byteBuffer.getShort(); + struct.ndm_ifindex = byteBuffer.getInt(); + struct.ndm_state = byteBuffer.getShort(); + struct.ndm_flags = byteBuffer.get(); + struct.ndm_type = byteBuffer.get(); + return struct; + } + + public byte ndm_family; + public int ndm_ifindex; + public short ndm_state; + public byte ndm_flags; + public byte ndm_type; + + public StructNdMsg() { + ndm_family = (byte) OsConstants.AF_UNSPEC; + } + + public boolean pack(ByteBuffer byteBuffer) { + if (!hasAvailableSpace(byteBuffer)) { return false; } + + // The ByteOrder must have already been set by the caller. In most + // cases ByteOrder.nativeOrder() is correct, with the exception + // of usage within unittests. + byteBuffer.put(ndm_family); + byteBuffer.put((byte) 0); // pad1 + byteBuffer.putShort((short) 0); // pad2 + byteBuffer.putInt(ndm_ifindex); + byteBuffer.putShort(ndm_state); + byteBuffer.put(ndm_flags); + byteBuffer.put(ndm_type); + return true; + } + + public boolean nudConnected() { + return isNudStateConnected(ndm_state); + } + + public boolean nudValid() { + return (nudConnected() || ((ndm_state & (NUD_PROBE|NUD_STALE|NUD_DELAY)) != 0)); + } + + @Override + public String toString() { + final String stateStr = "" + ndm_state + " (" + stringForNudState(ndm_state) + ")"; + final String flagsStr = "" + ndm_flags + " (" + stringForNudFlags(ndm_flags) + ")"; + return "StructNdMsg{ " + + "family{" + NetlinkConstants.stringForAddressFamily((int) ndm_family) + "}, " + + "ifindex{" + ndm_ifindex + "}, " + + "state{" + stateStr + "}, " + + "flags{" + flagsStr + "}, " + + "type{" + ndm_type + "} " + + "}"; + } +} diff --git a/core/java/android/net/netlink/StructNdaCacheInfo.java b/core/java/android/net/netlink/StructNdaCacheInfo.java new file mode 100644 index 0000000..16cf563 --- /dev/null +++ b/core/java/android/net/netlink/StructNdaCacheInfo.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.system.Os; +import android.system.OsConstants; + +import java.nio.ByteBuffer; + + +/** + * struct nda_cacheinfo + * + * see: <linux_src>/include/uapi/linux/neighbour.h + * + * @hide + */ +public class StructNdaCacheInfo { + // Already aligned. + public static final int STRUCT_SIZE = 16; + + private static boolean hasAvailableSpace(ByteBuffer byteBuffer) { + return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE; + } + + public static StructNdaCacheInfo parse(ByteBuffer byteBuffer) { + if (!hasAvailableSpace(byteBuffer)) { return null; } + + // The ByteOrder must have already been set by the caller. In most + // cases ByteOrder.nativeOrder() is correct, with the possible + // exception of usage within unittests. + final StructNdaCacheInfo struct = new StructNdaCacheInfo(); + struct.ndm_used = byteBuffer.getInt(); + struct.ndm_confirmed = byteBuffer.getInt(); + struct.ndm_updated = byteBuffer.getInt(); + struct.ndm_refcnt = byteBuffer.getInt(); + return struct; + } + + // TODO: investigate whether this can change during device runtime and + // decide what (if anything) should be done about that. + private static final long CLOCK_TICKS_PER_SECOND = Os.sysconf(OsConstants._SC_CLK_TCK); + + private static long ticksToMilliSeconds(int intClockTicks) { + final long longClockTicks = (long) intClockTicks & 0xffffffff; + return (longClockTicks * 1000) / CLOCK_TICKS_PER_SECOND; + } + + /** + * Explanatory notes, for reference. + * + * Before being returned to user space, the neighbor entry times are + * converted to clock_t's like so: + * + * ndm_used = jiffies_to_clock_t(now - neigh->used); + * ndm_confirmed = jiffies_to_clock_t(now - neigh->confirmed); + * ndm_updated = jiffies_to_clock_t(now - neigh->updated); + * + * meaning that these values are expressed as "clock ticks ago". To + * convert these clock ticks to seconds divide by sysconf(_SC_CLK_TCK). + * When _SC_CLK_TCK is 100, for example, the ndm_* times are expressed + * in centiseconds. + * + * These values are unsigned, but fortunately being expressed as "some + * clock ticks ago", these values are typically very small (and + * 2^31 centiseconds = 248 days). + * + * By observation, it appears that: + * ndm_used: the last time ARP/ND took place for this neighbor + * ndm_confirmed: the last time ARP/ND succeeded for this neighbor OR + * higher layer confirmation (TCP or MSG_CONFIRM) + * was received + * ndm_updated: the time when the current NUD state was entered + */ + public int ndm_used; + public int ndm_confirmed; + public int ndm_updated; + public int ndm_refcnt; + + public StructNdaCacheInfo() {} + + public long lastUsed() { + return ticksToMilliSeconds(ndm_used); + } + + public long lastConfirmed() { + return ticksToMilliSeconds(ndm_confirmed); + } + + public long lastUpdated() { + return ticksToMilliSeconds(ndm_updated); + } + + @Override + public String toString() { + return "NdaCacheInfo{ " + + "ndm_used{" + lastUsed() + "}, " + + "ndm_confirmed{" + lastConfirmed() + "}, " + + "ndm_updated{" + lastUpdated() + "}, " + + "ndm_refcnt{" + ndm_refcnt + "} " + + "}"; + } +} diff --git a/core/java/android/net/netlink/StructNlAttr.java b/core/java/android/net/netlink/StructNlAttr.java new file mode 100644 index 0000000..9aef4c7 --- /dev/null +++ b/core/java/android/net/netlink/StructNlAttr.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.net.netlink.NetlinkConstants; +import libcore.io.SizeOf; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteOrder; +import java.nio.ByteBuffer; + + +/** + * struct nlattr + * + * see: <linux_src>/include/uapi/linux/netlink.h + * + * @hide + */ +public class StructNlAttr { + // Already aligned. + public static final int NLA_HEADERLEN = 4; + + // Return a (length, type) object only, without consuming any bytes in + // |byteBuffer| and without copying or interpreting any value bytes. + // This is used for scanning over a packed set of struct nlattr's, + // looking for instances of a particular type. + public static StructNlAttr peek(ByteBuffer byteBuffer) { + if (byteBuffer == null || byteBuffer.remaining() < NLA_HEADERLEN) { + return null; + } + final int baseOffset = byteBuffer.position(); + + final StructNlAttr struct = new StructNlAttr(); + struct.nla_len = byteBuffer.getShort(); + struct.nla_type = byteBuffer.getShort(); + struct.mByteOrder = byteBuffer.order(); + + byteBuffer.position(baseOffset); + if (struct.nla_len < NLA_HEADERLEN) { + // Malformed. + return null; + } + return struct; + } + + public static StructNlAttr parse(ByteBuffer byteBuffer) { + final StructNlAttr struct = peek(byteBuffer); + if (struct == null || byteBuffer.remaining() < struct.getAlignedLength()) { + return null; + } + + final int baseOffset = byteBuffer.position(); + byteBuffer.position(baseOffset + NLA_HEADERLEN); + + int valueLen = ((int) struct.nla_len) & 0xffff; + valueLen -= NLA_HEADERLEN; + if (valueLen > 0) { + struct.nla_value = new byte[valueLen]; + byteBuffer.get(struct.nla_value, 0, valueLen); + byteBuffer.position(baseOffset + struct.getAlignedLength()); + } + return struct; + } + + public short nla_len; + public short nla_type; + public byte[] nla_value; + public ByteOrder mByteOrder; + + public StructNlAttr() { + mByteOrder = ByteOrder.nativeOrder(); + } + + public int getAlignedLength() { + return NetlinkConstants.alignedLengthOf(nla_len); + } + + public ByteBuffer getValueAsByteBuffer() { + if (nla_value == null) { return null; } + final ByteBuffer byteBuffer = ByteBuffer.wrap(nla_value); + byteBuffer.order(mByteOrder); + return byteBuffer; + } + + public int getValueAsInt(int defaultValue) { + final ByteBuffer byteBuffer = getValueAsByteBuffer(); + if (byteBuffer == null || byteBuffer.remaining() != SizeOf.INT) { + return defaultValue; + } + return getValueAsByteBuffer().getInt(); + } + + public InetAddress getValueAsInetAddress() { + if (nla_value == null) { return null; } + + try { + return InetAddress.getByAddress(nla_value); + } catch (UnknownHostException ignored) { + return null; + } + } + + @Override + public String toString() { + return "StructNlAttr{ " + + "nla_len{" + nla_len + "}, " + + "nla_type{" + nla_type + "}, " + + "nla_value{" + NetlinkConstants.hexify(nla_value) + "}, " + + "}"; + } +} diff --git a/core/java/android/net/netlink/StructNlMsgErr.java b/core/java/android/net/netlink/StructNlMsgErr.java new file mode 100644 index 0000000..5da19a2 --- /dev/null +++ b/core/java/android/net/netlink/StructNlMsgErr.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.net.netlink.NetlinkConstants; +import android.net.netlink.StructNlMsgHdr; +import libcore.io.SizeOf; + +import java.nio.ByteBuffer; + + +/** + * struct nlmsgerr + * + * see <linux_src>/include/uapi/linux/netlink.h + * + * @hide + */ +public class StructNlMsgErr { + public static final int STRUCT_SIZE = SizeOf.INT + StructNlMsgHdr.STRUCT_SIZE; + + public static boolean hasAvailableSpace(ByteBuffer byteBuffer) { + return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE; + } + + public static StructNlMsgErr parse(ByteBuffer byteBuffer) { + if (!hasAvailableSpace(byteBuffer)) { return null; } + + // The ByteOrder must have already been set by the caller. In most + // cases ByteOrder.nativeOrder() is correct, with the exception + // of usage within unittests. + final StructNlMsgErr struct = new StructNlMsgErr(); + struct.error = byteBuffer.getInt(); + struct.msg = StructNlMsgHdr.parse(byteBuffer); + return struct; + } + + public int error; + public StructNlMsgHdr msg; + + public StructNlMsgErr() { + error = 0; + msg = null; + } + + public boolean pack(ByteBuffer byteBuffer) { + if (!hasAvailableSpace(byteBuffer)) { return false; } + + // The ByteOrder must have already been set by the caller. In most + // cases ByteOrder.nativeOrder() is correct, with the possible + // exception of usage within unittests. + byteBuffer.putInt(error); + if (msg != null) { + msg.pack(byteBuffer); + } + return true; + } + + @Override + public String toString() { + return "StructNlMsgErr{ " + + "error{" + error + "}, " + + "msg{" + (msg == null ? "" : msg.toString()) + "} " + + "}"; + } +} diff --git a/core/java/android/net/netlink/StructNlMsgHdr.java b/core/java/android/net/netlink/StructNlMsgHdr.java new file mode 100644 index 0000000..67925ac --- /dev/null +++ b/core/java/android/net/netlink/StructNlMsgHdr.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.netlink; + +import android.net.netlink.NetlinkConstants; +import java.nio.ByteBuffer; + + +/** + * struct nlmsghdr + * + * see <linux_src>/include/uapi/linux/netlink.h + * + * @hide + */ +public class StructNlMsgHdr { + // Already aligned. + public static final int STRUCT_SIZE = 16; + + public static final short NLM_F_REQUEST = 0x0001; + public static final short NLM_F_MULTI = 0x0002; + public static final short NLM_F_ACK = 0x0004; + public static final short NLM_F_ECHO = 0x0008; + // Flags for a GET request. + public static final short NLM_F_ROOT = 0x0100; + public static final short NLM_F_MATCH = 0x0200; + public static final short NLM_F_DUMP = NLM_F_ROOT|NLM_F_MATCH; + + public static String stringForNlMsgFlags(short flags) { + final StringBuilder sb = new StringBuilder(); + if ((flags & NLM_F_REQUEST) != 0) { + sb.append("NLM_F_REQUEST"); + } + if ((flags & NLM_F_MULTI) != 0) { + if (sb.length() > 0) { sb.append("|"); } + sb.append("NLM_F_MULTI"); + } + if ((flags & NLM_F_ACK) != 0) { + if (sb.length() > 0) { sb.append("|"); } + sb.append("NLM_F_ACK"); + } + if ((flags & NLM_F_ECHO) != 0) { + if (sb.length() > 0) { sb.append("|"); } + sb.append("NLM_F_ECHO"); + } + if ((flags & NLM_F_ROOT) != 0) { + if (sb.length() > 0) { sb.append("|"); } + sb.append("NLM_F_ROOT"); + } + if ((flags & NLM_F_MATCH) != 0) { + if (sb.length() > 0) { sb.append("|"); } + sb.append("NLM_F_MATCH"); + } + return sb.toString(); + } + + public static boolean hasAvailableSpace(ByteBuffer byteBuffer) { + return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE; + } + + public static StructNlMsgHdr parse(ByteBuffer byteBuffer) { + if (!hasAvailableSpace(byteBuffer)) { return null; } + + // The ByteOrder must have already been set by the caller. In most + // cases ByteOrder.nativeOrder() is correct, with the exception + // of usage within unittests. + final StructNlMsgHdr struct = new StructNlMsgHdr(); + struct.nlmsg_len = byteBuffer.getInt(); + struct.nlmsg_type = byteBuffer.getShort(); + struct.nlmsg_flags = byteBuffer.getShort(); + struct.nlmsg_seq = byteBuffer.getInt(); + struct.nlmsg_pid = byteBuffer.getInt(); + + if (struct.nlmsg_len < STRUCT_SIZE) { + // Malformed. + return null; + } + return struct; + } + + public int nlmsg_len; + public short nlmsg_type; + public short nlmsg_flags; + public int nlmsg_seq; + public int nlmsg_pid; + + public StructNlMsgHdr() { + nlmsg_len = 0; + nlmsg_type = 0; + nlmsg_flags = 0; + nlmsg_seq = 0; + nlmsg_pid = 0; + } + + public boolean pack(ByteBuffer byteBuffer) { + if (!hasAvailableSpace(byteBuffer)) { return false; } + + // The ByteOrder must have already been set by the caller. In most + // cases ByteOrder.nativeOrder() is correct, with the possible + // exception of usage within unittests. + byteBuffer.putInt(nlmsg_len); + byteBuffer.putShort(nlmsg_type); + byteBuffer.putShort(nlmsg_flags); + byteBuffer.putInt(nlmsg_seq); + byteBuffer.putInt(nlmsg_pid); + return true; + } + + @Override + public String toString() { + final String typeStr = "" + nlmsg_type + + "(" + NetlinkConstants.stringForNlMsgType(nlmsg_type) + ")"; + final String flagsStr = "" + nlmsg_flags + + "(" + stringForNlMsgFlags(nlmsg_flags) + ")"; + return "StructNlMsgHdr{ " + + "nlmsg_len{" + nlmsg_len + "}, " + + "nlmsg_type{" + typeStr + "}, " + + "nlmsg_flags{" + flagsStr + ")}, " + + "nlmsg_seq{" + nlmsg_seq + "}, " + + "nlmsg_pid{" + nlmsg_pid + "} " + + "}"; + } +} diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 4dfe0de..5f515eb 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -425,6 +425,24 @@ public abstract class BatteryStats implements Parcelable { public abstract long getMobileRadioActiveTime(int which); public abstract int getMobileRadioActiveCount(int which); + /** + * Get the total cpu time (in microseconds) this UID had processes executing in userspace. + */ + public abstract long getUserCpuTimeUs(int which); + + /** + * Get the total cpu time (in microseconds) this UID had processes executing kernel syscalls. + */ + public abstract long getSystemCpuTimeUs(int which); + + /** + * Returns the approximate cpu time (in milliseconds) spent at a certain CPU speed. + * @param speedStep the index of the CPU speed. This is not the actual speed of the CPU. + * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT. + * @see BatteryStats#getCpuSpeedSteps() + */ + public abstract long getTimeAtCpuSpeed(int step, int which); + public static abstract class Sensor { /* * FIXME: it's not correct to use this magic value because it @@ -506,15 +524,6 @@ public abstract class BatteryStats implements Parcelable { */ public abstract long getForegroundTime(int which); - /** - * Returns the approximate cpu time (in milliseconds) spent at a certain CPU speed. - * @param speedStep the index of the CPU speed. This is not the actual speed of the - * CPU. - * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT. - * @see BatteryStats#getCpuSpeedSteps() - */ - public abstract long getTimeAtCpuSpeedStep(int speedStep, int which); - public abstract int countExcessivePowers(); public abstract ExcessivePower getExcessivePower(int i); @@ -3873,6 +3882,16 @@ public abstract class BatteryStats implements Parcelable { } } + final long userCpuTimeUs = u.getUserCpuTimeUs(which); + final long systemCpuTimeUs = u.getSystemCpuTimeUs(which); + if (userCpuTimeUs > 0 || systemCpuTimeUs > 0) { + sb.setLength(0); + sb.append(prefix); + sb.append(" Total cpu time: "); + formatTimeMs(sb, (userCpuTimeUs + systemCpuTimeUs) / 1000); + pw.println(sb.toString()); + } + final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); for (int ipr=processStats.size()-1; ipr>=0; ipr--) { diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 021e5e4..917271d 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -528,7 +528,7 @@ public class FileUtils { * {@code /after/foo/bar.txt}. */ public static File rewriteAfterRename(File beforeDir, File afterDir, File file) { - if (file == null) return null; + if (file == null || beforeDir == null || afterDir == null) return null; if (contains(beforeDir, file)) { final String splice = file.getAbsolutePath().substring( beforeDir.getAbsolutePath().length()); diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl index 3cb29ff..602bfea 100644 --- a/core/java/android/os/IDeviceIdleController.aidl +++ b/core/java/android/os/IDeviceIdleController.aidl @@ -23,4 +23,5 @@ interface IDeviceIdleController { String[] getSystemPowerWhitelist(); String[] getFullPowerWhitelist(); int[] getAppIdWhitelist(); + boolean isPowerSaveWhitelistApp(String name); } diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index 00ab262..9a0d0d0 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -134,4 +134,6 @@ public abstract class PowerManagerInternal { } public abstract void setDeviceIdleMode(boolean enabled); + + public abstract void setDeviceIdleWhitelist(int[] appids); } diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 0b55998..1cc2d33 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -476,8 +476,18 @@ public final class StrictMode { } /** - * Enable detection of mismatches between defined resource types + * Enables detection of mismatches between defined resource types * and getter calls. + * <p> + * This helps detect accidental type mismatches and potentially + * expensive type conversions when obtaining typed resources. + * <p> + * For example, a strict mode violation would be thrown when + * calling {@link android.content.res.TypedArray#getInt(int, int)} + * on an index that contains a String-type resource. If the string + * value can be parsed as an integer, this method call will return + * a value without crashing; however, the developer should format + * the resource as an integer to avoid unnecessary type conversion. */ public Builder detectResourceMismatches() { return enable(DETECT_RESOURCE_MISMATCH); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index cc37d5e..3dee68c 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -936,7 +936,7 @@ public class UserManager { * Returns list of the profiles of userHandle including * userHandle itself. * Note that this returns both enabled and not enabled profiles. See - * {@link #getUserProfiles()} if you need only the enabled ones. + * {@link #getEnabledProfiles(int)} if you need only the enabled ones. * * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. * @param userHandle profiles of this user will be returned. @@ -953,6 +953,25 @@ public class UserManager { } /** + * Returns list of the profiles of userHandle including + * userHandle itself. + * Note that this returns only enabled. + * + * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. + * @param userHandle profiles of this user will be returned. + * @return the list of profiles. + * @hide + */ + public List<UserInfo> getEnabledProfiles(int userHandle) { + try { + return mService.getProfiles(userHandle, true /* enabledOnly */); + } catch (RemoteException re) { + Log.w(TAG, "Could not get user list", re); + return null; + } + } + + /** * Returns a list of UserHandles for profiles associated with the user that the calling process * is running on, including the user itself. * diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index fcde3f4..e55ae99 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -1005,6 +1005,22 @@ public interface IMountService extends IInterface { } @Override + public long benchmark(String volId) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeString(volId); + mRemote.transact(Stub.TRANSACTION_benchmark, _data, _reply, 0); + _reply.readException(); + return _reply.readLong(); + } finally { + _reply.recycle(); + _data.recycle(); + } + } + + @Override public void partitionPublic(String diskId) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); @@ -1099,6 +1115,36 @@ public interface IMountService extends IInterface { } @Override + public void forgetAllVolumes() throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + try { + _data.writeInterfaceToken(DESCRIPTOR); + mRemote.transact(Stub.TRANSACTION_forgetAllVolumes, _data, _reply, 0); + _reply.readException(); + } finally { + _reply.recycle(); + _data.recycle(); + } + } + + @Override + public void setDebugFlags(int _flags, int _mask) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeInt(_flags); + _data.writeInt(_mask); + mRemote.transact(Stub.TRANSACTION_setDebugFlags, _data, _reply, 0); + _reply.readException(); + } finally { + _reply.recycle(); + _data.recycle(); + } + } + + @Override public String getPrimaryStorageUuid() throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); @@ -1238,9 +1284,13 @@ public interface IMountService extends IInterface { static final int TRANSACTION_setVolumeNickname = IBinder.FIRST_CALL_TRANSACTION + 53; static final int TRANSACTION_setVolumeUserFlags = IBinder.FIRST_CALL_TRANSACTION + 54; static final int TRANSACTION_forgetVolume = IBinder.FIRST_CALL_TRANSACTION + 55; + static final int TRANSACTION_forgetAllVolumes = IBinder.FIRST_CALL_TRANSACTION + 56; - static final int TRANSACTION_getPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 56; - static final int TRANSACTION_setPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 57; + static final int TRANSACTION_getPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 57; + static final int TRANSACTION_setPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 58; + + static final int TRANSACTION_benchmark = IBinder.FIRST_CALL_TRANSACTION + 59; + static final int TRANSACTION_setDebugFlags = IBinder.FIRST_CALL_TRANSACTION + 60; /** * Cast an IBinder object into an IMountService interface, generating a @@ -1711,6 +1761,14 @@ public interface IMountService extends IInterface { reply.writeNoException(); return true; } + case TRANSACTION_benchmark: { + data.enforceInterface(DESCRIPTOR); + String volId = data.readString(); + long res = benchmark(volId); + reply.writeNoException(); + reply.writeLong(res); + return true; + } case TRANSACTION_partitionPublic: { data.enforceInterface(DESCRIPTOR); String diskId = data.readString(); @@ -1757,6 +1815,20 @@ public interface IMountService extends IInterface { reply.writeNoException(); return true; } + case TRANSACTION_forgetAllVolumes: { + data.enforceInterface(DESCRIPTOR); + forgetAllVolumes(); + reply.writeNoException(); + return true; + } + case TRANSACTION_setDebugFlags: { + data.enforceInterface(DESCRIPTOR); + int _flags = data.readInt(); + int _mask = data.readInt(); + setDebugFlags(_flags, _mask); + reply.writeNoException(); + return true; + } case TRANSACTION_getPrimaryStorageUuid: { data.enforceInterface(DESCRIPTOR); String volumeUuid = getPrimaryStorageUuid(); @@ -2067,6 +2139,7 @@ public interface IMountService extends IInterface { public void mount(String volId) throws RemoteException; public void unmount(String volId) throws RemoteException; public void format(String volId) throws RemoteException; + public long benchmark(String volId) throws RemoteException; public void partitionPublic(String diskId) throws RemoteException; public void partitionPrivate(String diskId) throws RemoteException; @@ -2075,6 +2148,8 @@ public interface IMountService extends IInterface { public void setVolumeNickname(String fsUuid, String nickname) throws RemoteException; public void setVolumeUserFlags(String fsUuid, int flags, int mask) throws RemoteException; public void forgetVolume(String fsUuid) throws RemoteException; + public void forgetAllVolumes() throws RemoteException; + public void setDebugFlags(int flags, int mask) throws RemoteException; public String getPrimaryStorageUuid() throws RemoteException; public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 3fdabee..8ff56f8 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -73,6 +73,8 @@ public class StorageManager { /** {@hide} */ public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical"; /** {@hide} */ + public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable"; + /** {@hide} */ public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable"; /** {@hide} */ @@ -80,6 +82,9 @@ public class StorageManager { /** {@hide} */ public static final String UUID_PRIMARY_PHYSICAL = "primary_physical"; + /** {@hide} */ + public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0; + private final Context mContext; private final ContentResolver mResolver; @@ -639,6 +644,15 @@ public class StorageManager { } /** {@hide} */ + public long benchmark(String volId) { + try { + return mMountService.benchmark(volId); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** {@hide} */ public void partitionPublic(String diskId) { try { mMountService.partitionPublic(diskId); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index dc70d7b..ef0dc3e 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3102,7 +3102,16 @@ public final class Settings { public static final String EGG_MODE = "egg_mode"; /** @hide */ - public static final Validator EGG_MODE_VALIDATOR = sBooleanValidator; + public static final Validator EGG_MODE_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + try { + return Long.parseLong(value) >= 0; + } catch (NumberFormatException e) { + return false; + } + } + }; /** * IMPORTANT: If you add a new public settings you also have to add it to @@ -4133,7 +4142,10 @@ public final class Settings { * LocationManager service for testing purposes during application development. These * locations and status values override actual location and status information generated * by network, gps, or other location providers. + * + * @deprecated This settings is not used anymore. */ + @Deprecated public static final String ALLOW_MOCK_LOCATION = "mock_location"; /** @@ -4363,12 +4375,6 @@ public final class Settings { "lock_screen_appwidget_ids"; /** - * List of enrolled fingerprint identifiers (comma-delimited). - * @hide - */ - public static final String USER_FINGERPRINT_IDS = "user_fingerprint_ids"; - - /** * Id of the appwidget shown on the lock screen when appwidgets are disabled. * @hide */ @@ -5517,6 +5523,12 @@ public final class Settings { public static final String APP_IDLE_DURATION = "app_idle_duration"; /** + * Controls whether double tap to wake is enabled. + * @hide + */ + public static final String DOUBLE_TAP_TO_WAKE = "double_tap_to_wake"; + + /** * This are the settings to be backed up. * * NOTE: Settings are backed up and restored in the order they appear @@ -5571,7 +5583,8 @@ public final class Settings { MOUNT_UMS_PROMPT, MOUNT_UMS_NOTIFY_ENABLED, UI_NIGHT_MODE, - SLEEP_TIMEOUT + SLEEP_TIMEOUT, + DOUBLE_TAP_TO_WAKE, }; /** diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java index 879f26c..89668c1 100644 --- a/core/java/android/provider/VoicemailContract.java +++ b/core/java/android/provider/VoicemailContract.java @@ -306,6 +306,13 @@ public class VoicemailContract { contentValues.put(Voicemails.SOURCE_PACKAGE, voicemail.getSourcePackage()); contentValues.put(Voicemails.SOURCE_DATA, voicemail.getSourceData()); contentValues.put(Voicemails.IS_READ, voicemail.isRead() ? 1 : 0); + + PhoneAccountHandle phoneAccount = voicemail.getPhoneAccount(); + if (voicemail.getPhoneAccount() != null) { + contentValues.put(Voicemails.PHONE_ACCOUNT_COMPONENT_NAME, + phoneAccount.getComponentName().flattenToString()); + contentValues.put(Voicemails.PHONE_ACCOUNT_ID, phoneAccount.getId()); + } return contentValues; } } diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl index 4809050..b0779c0 100644 --- a/core/java/android/security/IKeystoreService.aidl +++ b/core/java/android/security/IKeystoreService.aidl @@ -75,4 +75,6 @@ interface IKeystoreService { int abort(IBinder handle); boolean isOperationAuthorized(IBinder token); int addAuthToken(in byte[] authToken); + int onUserAdded(int userId, int parentId); + int onUserRemoved(int userId); } diff --git a/core/java/android/service/carrier/CarrierConfigService.java b/core/java/android/service/carrier/CarrierConfigService.java index 1880d16..bf33ad5 100644 --- a/core/java/android/service/carrier/CarrierConfigService.java +++ b/core/java/android/service/carrier/CarrierConfigService.java @@ -16,21 +16,21 @@ package android.service.carrier; import android.app.Service; import android.content.Intent; -import android.os.Bundle; import android.os.IBinder; +import android.os.PersistableBundle; /** * A service that sets carrier configuration for telephony services. * <p> * To extend this class, you must declare the service in your manifest file to require the - * {@link android.Manifest.permission#BIND_CARRIER_CONFIG_SERVICE} permission and include an intent + * {@link android.Manifest.permission#BIND_CARRIER_SERVICES} permission and include an intent * filter with the {@link #SERVICE_INTERFACE} action. For example: * </p> * * <pre>{@code * <service android:name=".MyCarrierConfigService" * android:label="@string/service_name" - * android:permission="android.permission.BIND_CARRIER_CONFIG_SERVICE"> + * android:permission="android.permission.BIND_CARRIER_SERVICES"> * <intent-filter> * <action android:name="android.service.carrier.CarrierConfigService" /> * </intent-filter> @@ -68,16 +68,16 @@ public abstract class CarrierConfigService extends Service { * </p> * <p> * Implementations should use the keys defined in {@link android.telephony.CarrierConfigManager - * CarrierConfigManager}. Any configuration values not set in the returned {@link Bundle} may be - * overridden by the system's default configuration service. + * CarrierConfigManager}. Any configuration values not set in the returned {@link + * PersistableBundle} may be overridden by the system's default configuration service. * </p> * * @param id contains details about the current carrier that can be used do decide what * configuration values to return. - * @return a {@link Bundle} object containing the configuration or null if default values should - * be used. + * @return a {@link PersistableBundle} object containing the configuration or null if default + * values should be used. */ - public abstract Bundle onLoadConfig(CarrierIdentifier id); + public abstract PersistableBundle onLoadConfig(CarrierIdentifier id); /** @hide */ @Override @@ -97,7 +97,7 @@ public abstract class CarrierConfigService extends Service { private class ICarrierConfigServiceWrapper extends ICarrierConfigService.Stub { @Override - public Bundle getCarrierConfig(CarrierIdentifier id) { + public PersistableBundle getCarrierConfig(CarrierIdentifier id) { return CarrierConfigService.this.onLoadConfig(id); } } diff --git a/core/java/android/service/carrier/CarrierMessagingService.java b/core/java/android/service/carrier/CarrierMessagingService.java index d7bf10c..f5396a3 100644 --- a/core/java/android/service/carrier/CarrierMessagingService.java +++ b/core/java/android/service/carrier/CarrierMessagingService.java @@ -31,12 +31,12 @@ import java.util.List; * A service that receives calls from the system when new SMS and MMS are * sent or received. * <p>To extend this class, you must declare the service in your manifest file with - * the {@link android.Manifest.permission#BIND_CARRIER_MESSAGING_SERVICE} permission + * the {@link android.Manifest.permission#BIND_CARRIER_SERVICES} permission * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p> * <pre> * <service android:name=".MyMessagingService" * android:label="@string/service_name" - * android:permission="android.permission.BIND_CARRIER_MESSAGING_SERVICE"> + * android:permission="android.permission.BIND_CARRIER_SERVICES"> * <intent-filter> * <action android:name="android.service.carrier.CarrierMessagingService" /> * </intent-filter> diff --git a/core/java/android/service/carrier/ICarrierConfigService.aidl b/core/java/android/service/carrier/ICarrierConfigService.aidl index d8390b6..abbc000 100644 --- a/core/java/android/service/carrier/ICarrierConfigService.aidl +++ b/core/java/android/service/carrier/ICarrierConfigService.aidl @@ -16,7 +16,7 @@ package android.service.carrier; -import android.os.Bundle; +import android.os.PersistableBundle; import android.service.carrier.CarrierIdentifier; /** @@ -28,5 +28,5 @@ import android.service.carrier.CarrierIdentifier; interface ICarrierConfigService { /** @see android.service.carrier.CarrierConfigService#onLoadConfig */ - Bundle getCarrierConfig(in CarrierIdentifier id); -}
\ No newline at end of file + PersistableBundle getCarrierConfig(in CarrierIdentifier id); +} diff --git a/core/java/android/service/chooser/ChooserTarget.java b/core/java/android/service/chooser/ChooserTarget.java index 4c94ee7..50c435a 100644 --- a/core/java/android/service/chooser/ChooserTarget.java +++ b/core/java/android/service/chooser/ChooserTarget.java @@ -25,6 +25,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.graphics.Bitmap; +import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -34,6 +35,16 @@ import android.util.Log; /** * A ChooserTarget represents a deep-link into an application as returned by a * {@link android.service.chooser.ChooserTargetService}. + * + * <p>A chooser target represents a specific deep link target into an application exposed + * for selection by the user. This might be a frequently emailed contact, a recently active + * group messaging conversation, a folder in a cloud storage app, a collection of related + * items published on a social media service or any other contextually relevant grouping + * of target app + relevant metadata.</p> + * + * <p>Creators of chooser targets should consult the relevant design guidelines for the type + * of target they are presenting. For example, targets involving people should be presented + * with a circular icon.</p> */ public final class ChooserTarget implements Parcelable { private static final String TAG = "ChooserTarget"; @@ -48,7 +59,7 @@ public final class ChooserTarget implements Parcelable { * The icon that will be shown to the user to represent this target. * The system may resize this icon as appropriate. */ - private Bitmap mIcon; + private Icon mIcon; /** * The IntentSender that will be used to deliver the intent to the target. @@ -93,7 +104,7 @@ public final class ChooserTarget implements Parcelable { * @param score ranking score for this target between 0.0f and 1.0f, inclusive * @param pendingIntent PendingIntent to fill in and send if the user chooses this target */ - public ChooserTarget(CharSequence title, Bitmap icon, float score, + public ChooserTarget(CharSequence title, Icon icon, float score, PendingIntent pendingIntent) { this(title, icon, score, pendingIntent.getIntentSender()); } @@ -129,7 +140,7 @@ public final class ChooserTarget implements Parcelable { * @param score ranking score for this target between 0.0f and 1.0f, inclusive * @param intentSender IntentSender to fill in and send if the user chooses this target */ - public ChooserTarget(CharSequence title, Bitmap icon, float score, IntentSender intentSender) { + public ChooserTarget(CharSequence title, Icon icon, float score, IntentSender intentSender) { mTitle = title; mIcon = icon; if (score > 1.f || score < 0.f) { @@ -143,7 +154,7 @@ public final class ChooserTarget implements Parcelable { ChooserTarget(Parcel in) { mTitle = in.readCharSequence(); if (in.readInt() != 0) { - mIcon = Bitmap.CREATOR.createFromParcel(in); + mIcon = Icon.CREATOR.createFromParcel(in); } else { mIcon = null; } @@ -167,7 +178,7 @@ public final class ChooserTarget implements Parcelable { * * @return the icon representing this target, intended to be shown to a user */ - public Bitmap getIcon() { + public Icon getIcon() { return mIcon; } diff --git a/core/java/android/service/chooser/ChooserTargetService.java b/core/java/android/service/chooser/ChooserTargetService.java index 699bd0a..0d1834a 100644 --- a/core/java/android/service/chooser/ChooserTargetService.java +++ b/core/java/android/service/chooser/ChooserTargetService.java @@ -107,7 +107,7 @@ public abstract class ChooserTargetService extends Service { * <p>The returned list should be sorted such that the most relevant targets appear first. * Any PendingIntents used to construct the resulting ChooserTargets should always be prepared * to have the relevant data fields filled in by the sender. See - * {@link ChooserTarget#ChooserTarget(CharSequence, android.graphics.Bitmap, float, android.app.PendingIntent) ChooserTarget}.</p> + * {@link ChooserTarget#ChooserTarget(CharSequence, android.graphics.drawable.Icon, float, android.app.PendingIntent) ChooserTarget}.</p> * * <p><em>Important:</em> Calls to this method from other applications will occur on * a binder thread, not on your app's main thread. Make sure that access to relevant data diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 7956a3f..b8493d4 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -27,6 +27,10 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ParceledListSlice; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.graphics.Bitmap; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; @@ -322,7 +326,7 @@ public abstract class NotificationListenerService extends Service { if (!isBound()) return; try { getNotificationInterface().cancelNotificationsFromListener(mWrapper, - new String[] {key}); + new String[] { key }); } catch (android.os.RemoteException ex) { Log.v(TAG, "Unable to contact notification manager", ex); } @@ -464,6 +468,8 @@ public abstract class NotificationListenerService extends Service { for (int i = 0; i < N; i++) { Notification notification = list.get(i).getNotification(); Builder.rebuild(getContext(), notification); + // convert icon metadata to legacy format for older clients + createLegacyIconExtras(notification); } return list.toArray(new StatusBarNotification[N]); } catch (android.os.RemoteException ex) { @@ -636,6 +642,24 @@ public abstract class NotificationListenerService extends Service { } } + /** Convert new-style Icons to legacy representations for pre-M clients. */ + private void createLegacyIconExtras(Notification n) { + Icon smallIcon = n.getSmallIcon(); + Icon largeIcon = n.getLargeIcon(); + if (smallIcon.getType() == Icon.TYPE_RESOURCE) { + n.extras.putInt(Notification.EXTRA_SMALL_ICON, smallIcon.getResId()); + n.icon = smallIcon.getResId(); + } + if (largeIcon != null) { + Drawable d = largeIcon.loadDrawable(getContext()); + if (d != null && d instanceof BitmapDrawable) { + final Bitmap largeIconBits = ((BitmapDrawable) d).getBitmap(); + n.extras.putParcelable(Notification.EXTRA_LARGE_ICON, largeIconBits); + n.largeIcon = largeIconBits; + } + } + } + private class INotificationListenerWrapper extends INotificationListener.Stub { @Override public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder, @@ -649,6 +673,9 @@ public abstract class NotificationListenerService extends Service { } Notification.Builder.rebuild(getContext(), sbn.getNotification()); + // convert icon metadata to legacy format for older clients + createLegacyIconExtras(sbn.getNotification()); + // protect subclass from concurrent modifications of (@link mNotificationKeys}. synchronized (mWrapper) { applyUpdate(update); diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 599ac74..1e42913 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -573,24 +573,34 @@ public class ZenModeConfig implements Parcelable { } public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) { + return toTimeCondition(context, minutesFromNow, userHandle, false /*shortVersion*/); + } + + public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle, + boolean shortVersion) { final long now = System.currentTimeMillis(); final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS; - return toTimeCondition(context, now + millis, minutesFromNow, now, userHandle); + return toTimeCondition(context, now + millis, minutesFromNow, now, userHandle, + shortVersion); } public static Condition toTimeCondition(Context context, long time, int minutes, long now, - int userHandle) { + int userHandle, boolean shortVersion) { final int num, summaryResId, line1ResId; if (minutes < 60) { // display as minutes num = minutes; - summaryResId = R.plurals.zen_mode_duration_minutes_summary; - line1ResId = R.plurals.zen_mode_duration_minutes; + summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short + : R.plurals.zen_mode_duration_minutes_summary; + line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short + : R.plurals.zen_mode_duration_minutes; } else { // display as hours num = Math.round(minutes / 60f); - summaryResId = com.android.internal.R.plurals.zen_mode_duration_hours_summary; - line1ResId = com.android.internal.R.plurals.zen_mode_duration_hours; + summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short + : R.plurals.zen_mode_duration_hours_summary; + line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short + : R.plurals.zen_mode_duration_hours; } final String skeleton = DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma"; final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); @@ -793,17 +803,17 @@ public class ZenModeConfig implements Parcelable { } public static String getConditionLine1(Context context, ZenModeConfig config, - int userHandle) { - return getConditionLine(context, config, userHandle, true /*useLine1*/); + int userHandle, boolean shortVersion) { + return getConditionLine(context, config, userHandle, true /*useLine1*/, shortVersion); } public static String getConditionSummary(Context context, ZenModeConfig config, - int userHandle) { - return getConditionLine(context, config, userHandle, false /*useLine1*/); + int userHandle, boolean shortVersion) { + return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion); } private static String getConditionLine(Context context, ZenModeConfig config, - int userHandle, boolean useLine1) { + int userHandle, boolean useLine1, boolean shortVersion) { if (config == null) return ""; if (config.manualRule != null) { final Uri id = config.manualRule.conditionId; @@ -816,7 +826,7 @@ public class ZenModeConfig implements Parcelable { final long now = System.currentTimeMillis(); final long span = time - now; c = toTimeCondition(context, - time, Math.round(span / (float) MINUTES_MS), now, userHandle); + time, Math.round(span / (float) MINUTES_MS), now, userHandle, shortVersion); } final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary; return TextUtils.isEmpty(rt) ? "" : rt; diff --git a/core/java/android/text/BidiFormatter.java b/core/java/android/text/BidiFormatter.java index 2a2589a..7ea9da1 100644 --- a/core/java/android/text/BidiFormatter.java +++ b/core/java/android/text/BidiFormatter.java @@ -25,7 +25,7 @@ import java.util.Locale; /** * Utility class for formatting text for display in a potentially opposite-directionality context * without garbling. The directionality of the context is set at formatter creation and the - * directionality of the text can be either estimated or passed in when known. + * directionality of the text can be either estimated or passed in when known. * * <p>To support versions lower than {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, * you can use the support library's {@link android.support.v4.text.BidiFormatter} class. @@ -377,9 +377,11 @@ public final class BidiFormatter { * See {@link TextDirectionHeuristics} for pre-defined heuristics. * @param isolate Whether to directionally isolate the string to prevent it from garbling the * content around it - * @return Input string after applying the above processing. + * @return Input string after applying the above processing. {@code null} if {@code str} is + * {@code null}. */ public String unicodeWrap(String str, TextDirectionHeuristic heuristic, boolean isolate) { + if (str == null) return null; final boolean isRtl = heuristic.isRtl(str, 0, str.length()); StringBuilder result = new StringBuilder(); if (getStereoReset() && isolate) { diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index fc65f63..e99a960 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -79,7 +79,8 @@ public class DynamicLayout extends Layout boolean includepad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { this(base, display, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR, - spacingmult, spacingadd, includepad, StaticLayout.BREAK_STRATEGY_SIMPLE, + spacingmult, spacingadd, includepad, + StaticLayout.BREAK_STRATEGY_SIMPLE, StaticLayout.HYPHENATION_FREQUENCY_NONE, ellipsize, ellipsizedWidth); } @@ -96,7 +97,7 @@ public class DynamicLayout extends Layout TextPaint paint, int width, Alignment align, TextDirectionHeuristic textDir, float spacingmult, float spacingadd, - boolean includepad, int breakStrategy, + boolean includepad, int breakStrategy, int hyphenationFrequency, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) { super((ellipsize == null) ? display @@ -122,6 +123,7 @@ public class DynamicLayout extends Layout mIncludePad = includepad; mBreakStrategy = breakStrategy; + mHyphenationFrequency = hyphenationFrequency; /* * This is annoying, but we can't refer to the layout until @@ -293,7 +295,8 @@ public class DynamicLayout extends Layout .setLineSpacing(getSpacingAdd(), getSpacingMultiplier()) .setEllipsizedWidth(mEllipsizedWidth) .setEllipsize(mEllipsizeAt) - .setBreakStrategy(mBreakStrategy); + .setBreakStrategy(mBreakStrategy) + .setHyphenationFrequency(mHyphenationFrequency); reflowed.generate(b, false, true); int n = reflowed.getLineCount(); @@ -719,6 +722,7 @@ public class DynamicLayout extends Layout private int mEllipsizedWidth; private TextUtils.TruncateAt mEllipsizeAt; private int mBreakStrategy; + private int mHyphenationFrequency; private PackedIntVector mInts; private PackedObjectVector<Directions> mObjects; diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 60de02a..f176240 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -71,6 +71,35 @@ public abstract class Layout { */ public static final int BREAK_STRATEGY_BALANCED = 2; + /** @hide */ + @IntDef({HYPHENATION_FREQUENCY_NORMAL, HYPHENATION_FREQUENCY_FULL, + HYPHENATION_FREQUENCY_NONE}) + @Retention(RetentionPolicy.SOURCE) + public @interface HyphenationFrequency {} + + /** + * Value for hyphenation frequency indicating no automatic hyphenation. Useful + * for backward compatibility, and for cases where the automatic hyphenation algorithm results + * in incorrect hyphenation. Mid-word breaks may still happen when a word is wider than the + * layout and there is otherwise no valid break. Soft hyphens are ignored and will not be used + * as suggestions for potential line breaks. + */ + public static final int HYPHENATION_FREQUENCY_NONE = 0; + + /** + * Value for hyphenation frequency indicating a light amount of automatic hyphenation, which + * is a conservative default. Useful for informal cases, such as short sentences or chat + * messages. + */ + public static final int HYPHENATION_FREQUENCY_NORMAL = 1; + + /** + * Value for hyphenation frequency indicating the full amount of automatic hyphenation, typical + * in typography. Useful for running text and where it's important to put the maximum amount of + * text in a screen with limited space. + */ + public static final int HYPHENATION_FREQUENCY_FULL = 2; + private static final ParagraphStyle[] NO_PARA_SPANS = ArrayUtils.emptyArray(ParagraphStyle.class); diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 59c7c6d..d6d046b 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -92,6 +92,7 @@ public class StaticLayout extends Layout { b.mEllipsize = null; b.mMaxLines = Integer.MAX_VALUE; b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; + b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE; b.mMeasuredText = MeasuredText.obtain(); return b; @@ -276,6 +277,19 @@ public class StaticLayout extends Layout { } /** + * Set hyphenation frequency, to control the amount of automatic hyphenation used. The + * default is {@link Layout#HYPHENATION_FREQUENCY_NONE}. + * + * @param hyphenationFrequency hyphenation frequency for the paragraph + * @return this builder, useful for chaining + * @see android.widget.TextView#setHyphenationFrequency + */ + public Builder setHyphenationFrequency(@HyphenationFrequency int hyphenationFrequency) { + mHyphenationFrequency = hyphenationFrequency; + return this; + } + + /** * Set indents. Arguments are arrays holding an indent amount, one per line, measured in * pixels. For lines past the last element in the array, the last element repeats. * @@ -302,7 +316,8 @@ public class StaticLayout extends Layout { * the native code is as follows. * * For each paragraph, do a nSetupParagraph, which sets paragraph text, line width, tab - * stops, break strategy (and possibly other parameters in the future). + * stops, break strategy, and hyphenation frequency (and possibly other parameters in the + * future). * * Then, for each run within the paragraph: * - setLocale (this must be done at least for the first run, optional afterwards) @@ -377,6 +392,7 @@ public class StaticLayout extends Layout { TextUtils.TruncateAt mEllipsize; int mMaxLines; int mBreakStrategy; + int mHyphenationFrequency; Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt(); @@ -644,7 +660,7 @@ public class StaticLayout extends Layout { nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart, firstWidth, firstWidthLineCount, restWidth, - variableTabStops, TAB_INCREMENT, b.mBreakStrategy); + variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency); // measurement has to be done before performing line breaking // but we don't want to recompute fontmetrics or span ranges the @@ -1153,7 +1169,7 @@ public class StaticLayout extends Layout { // Set up paragraph text and settings; done as one big method to minimize jni crossings private static native void nSetupParagraph(long nativePtr, char[] text, int length, float firstWidth, int firstWidthLineCount, float restWidth, - int[] variableTabStops, int defaultTabStop, int breakStrategy); + int[] variableTabStops, int defaultTabStop, int breakStrategy, int hyphenationFrequency); private static native float nAddStyleRun(long nativePtr, long nativePaint, long nativeTypeface, int start, int end, boolean isRtl); diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java index c4dc5ed..5dda8a7 100644 --- a/core/java/android/text/method/WordIterator.java +++ b/core/java/android/text/method/WordIterator.java @@ -194,6 +194,87 @@ public class WordIterator implements Selection.PositionIterator { return BreakIterator.DONE; } + /** + * If <code>offset</code> is within a group of punctuation as defined + * by {@link #isPunctuation(int)}, returns the index of the first character + * of that group, otherwise returns BreakIterator.DONE. + * + * @param offset the offset to search from. + */ + public int getPunctuationBeginning(int offset) { + while (offset != BreakIterator.DONE && !isPunctuationStartBoundary(offset)) { + offset = prevBoundary(offset); + } + // No need to shift offset, prevBoundary handles that. + return offset; + } + + /** + * If <code>offset</code> is within a group of punctuation as defined + * by {@link #isPunctuation(int)}, returns the index of the last character + * of that group plus one, otherwise returns BreakIterator.DONE. + * + * @param offset the offset to search from. + */ + public int getPunctuationEnd(int offset) { + while (offset != BreakIterator.DONE && !isPunctuationEndBoundary(offset)) { + offset = nextBoundary(offset); + } + // No need to shift offset, nextBoundary handles that. + return offset; + } + + /** + * Indicates if the provided offset is after a punctuation character + * as defined by {@link #isPunctuation(int)}. + * + * @param offset the offset to check from. + * @return Whether the offset is after a punctuation character. + */ + public boolean isAfterPunctuation(int offset) { + final int shiftedOffset = offset - mOffsetShift; + if (shiftedOffset >= 1 && shiftedOffset <= mString.length()) { + final int codePoint = mString.codePointBefore(shiftedOffset); + return isPunctuation(codePoint); + } + return false; + } + + /** + * Indicates if the provided offset is at a punctuation character + * as defined by {@link #isPunctuation(int)}. + * + * @param offset the offset to check from. + * @return Whether the offset is at a punctuation character. + */ + public boolean isOnPunctuation(int offset) { + final int shiftedOffset = offset - mOffsetShift; + if (shiftedOffset >= 0 && shiftedOffset < mString.length()) { + final int codePoint = mString.codePointAt(shiftedOffset); + return isPunctuation(codePoint); + } + return false; + } + + private boolean isPunctuationStartBoundary(int offset) { + return isOnPunctuation(offset) && !isAfterPunctuation(offset); + } + + private boolean isPunctuationEndBoundary(int offset) { + return !isOnPunctuation(offset) && isAfterPunctuation(offset); + } + + private boolean isPunctuation(int cp) { + int type = Character.getType(cp); + return (type == Character.CONNECTOR_PUNCTUATION || + type == Character.DASH_PUNCTUATION || + type == Character.END_PUNCTUATION || + type == Character.FINAL_QUOTE_PUNCTUATION || + type == Character.INITIAL_QUOTE_PUNCTUATION || + type == Character.OTHER_PUNCTUATION || + type == Character.START_PUNCTUATION); + } + private boolean isAfterLetterOrDigit(int shiftedOffset) { if (shiftedOffset >= 1 && shiftedOffset <= mString.length()) { final int codePoint = mString.codePointBefore(shiftedOffset); diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 79a8489..d2b6533 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -209,7 +209,7 @@ public final class Choreographer { private static float getRefreshRate() { DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo( Display.DEFAULT_DISPLAY); - return di.refreshRate; + return di.getMode().getRefreshRate(); } /** diff --git a/core/java/com/android/internal/policy/IFaceLockCallback.aidl b/core/java/android/view/Display.aidl index 280e4d5..42bba44 100644 --- a/core/java/com/android/internal/policy/IFaceLockCallback.aidl +++ b/core/java/android/view/Display.aidl @@ -1,11 +1,11 @@ -/* - * Copyright (C) 2011 The Android Open Source Project +/** + * Copyright (c) 2015, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -13,14 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.internal.policy; -import android.os.IBinder; +package android.view; -/** {@hide} */ -oneway interface IFaceLockCallback { - void unlock(); - void cancel(); - void reportFailedAttempt(); - void pokeWakelock(int millis); -} +parcelable Display.Mode; diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 71e2251..5a587fe 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -21,6 +21,8 @@ import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; import android.hardware.display.DisplayManagerGlobal; +import android.os.Parcel; +import android.os.Parcelable; import android.os.Process; import android.os.SystemClock; import android.util.DisplayMetrics; @@ -170,6 +172,17 @@ public final class Display { public static final int FLAG_PRESENTATION = 1 << 3; /** + * Display flag: Indicates that the display has a round shape. + * <p> + * This flag identifies displays that are circular, elliptical or otherwise + * do not permit the user to see all the way to the logical corners of the display. + * </p> + * + * @see #getFlags + */ + public static final int FLAG_ROUND = 1 << 4; + + /** * Display flag: Indicates that the contents of the display should not be scaled * to fit the physical screen dimensions. Used for development only to emulate * devices with smaller physicals screens while preserving density. @@ -619,18 +632,44 @@ public final class Display { public float getRefreshRate() { synchronized (this) { updateDisplayInfoLocked(); - return mDisplayInfo.refreshRate; + return mDisplayInfo.getMode().getRefreshRate(); } } /** * Get the supported refresh rates of this display in frames per second. + * <p> + * This method only returns refresh rates for the display's default modes. For more options, use + * {@link #getSupportedModes()}. + * + * @deprecated use {@link #getSupportedModes()} instead */ + @Deprecated public float[] getSupportedRefreshRates() { synchronized (this) { updateDisplayInfoLocked(); - final float[] refreshRates = mDisplayInfo.supportedRefreshRates; - return Arrays.copyOf(refreshRates, refreshRates.length); + return mDisplayInfo.getDefaultRefreshRates(); + } + } + + /** + * Returns the active mode of the display. + */ + public Mode getMode() { + synchronized (this) { + updateDisplayInfoLocked(); + return mDisplayInfo.getMode(); + } + } + + /** + * Gets the supported modes of this display. + */ + public Mode[] getSupportedModes() { + synchronized (this) { + updateDisplayInfoLocked(); + final Display.Mode[] modes = mDisplayInfo.supportedModes; + return Arrays.copyOf(modes, modes.length); } } @@ -862,4 +901,152 @@ public final class Display { public static boolean isSuspendedState(int state) { return state == STATE_OFF || state == STATE_DOZE_SUSPEND; } + + /** + * A mode supported by a given display. + * + * @see Display#getSupportedModes() + */ + public static final class Mode implements Parcelable { + /** + * @hide + */ + public static final Mode[] EMPTY_ARRAY = new Mode[0]; + + private final int mModeId; + private final int mWidth; + private final int mHeight; + private final float mRefreshRate; + + /** + * @hide + */ + public Mode(int modeId, int width, int height, float refreshRate) { + mModeId = modeId; + mWidth = width; + mHeight = height; + mRefreshRate = refreshRate; + } + + /** + * Returns this mode's id. + */ + public int getModeId() { + return mModeId; + } + + /** + * Returns the physical width of the display in pixels when configured in this mode's + * resolution. + * <p> + * Note that due to application UI scaling, the number of pixels made available to + * applications when the mode is active (as reported by {@link Display#getWidth()} may + * differ from the mode's actual resolution (as reported by this function). + * <p> + * For example, applications running on a 4K display may have their UI laid out and rendered + * in 1080p and then scaled up. Applications can take advantage of the extra resolution by + * rendering content through a {@link android.view.SurfaceView} using full size buffers. + */ + public int getPhysicalWidth() { + return mWidth; + } + + /** + * Returns the physical height of the display in pixels when configured in this mode's + * resolution. + * <p> + * Note that due to application UI scaling, the number of pixels made available to + * applications when the mode is active (as reported by {@link Display#getHeight()} may + * differ from the mode's actual resolution (as reported by this function). + * <p> + * For example, applications running on a 4K display may have their UI laid out and rendered + * in 1080p and then scaled up. Applications can take advantage of the extra resolution by + * rendering content through a {@link android.view.SurfaceView} using full size buffers. + */ + public int getPhysicalHeight() { + return mHeight; + } + + /** + * Returns the refresh rate in frames per second. + */ + public float getRefreshRate() { + return mRefreshRate; + } + + /** + * Returns {@code true} if this mode matches the given parameters. + * + * @hide + */ + public boolean matches(int width, int height, float refreshRate) { + return mWidth == width && + mHeight == height && + Float.floatToIntBits(mRefreshRate) == Float.floatToIntBits(refreshRate); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof Mode)) { + return false; + } + Mode that = (Mode) other; + return mModeId == that.mModeId && matches(that.mWidth, that.mHeight, that.mRefreshRate); + } + + @Override + public int hashCode() { + int hash = 1; + hash = hash * 17 + mModeId; + hash = hash * 17 + mWidth; + hash = hash * 17 + mHeight; + hash = hash * 17 + Float.floatToIntBits(mRefreshRate); + return hash; + } + + @Override + public String toString() { + return new StringBuilder("{") + .append("id=").append(mModeId) + .append(", width=").append(mWidth) + .append(", height=").append(mHeight) + .append(", fps=").append(mRefreshRate) + .append("}") + .toString(); + } + + @Override + public int describeContents() { + return 0; + } + + private Mode(Parcel in) { + this(in.readInt(), in.readInt(), in.readInt(), in.readFloat()); + } + + @Override + public void writeToParcel(Parcel out, int parcelableFlags) { + out.writeInt(mModeId); + out.writeInt(mWidth); + out.writeInt(mHeight); + out.writeFloat(mRefreshRate); + } + + @SuppressWarnings("hiding") + public static final Parcelable.Creator<Mode> CREATOR + = new Parcelable.Creator<Mode>() { + @Override + public Mode createFromParcel(Parcel in) { + return new Mode(in); + } + + @Override + public Mode[] newArray(int size) { + return new Mode[size]; + } + }; + } } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 243961c..cf17990 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -20,11 +20,11 @@ import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.os.Parcel; import android.os.Parcelable; +import android.util.ArraySet; import android.util.DisplayMetrics; import java.util.Arrays; -import libcore.util.EmptyArray; import libcore.util.Objects; /** @@ -155,18 +155,19 @@ public final class DisplayInfo implements Parcelable { public int rotation; /** - * The refresh rate of this display in frames per second. - * <p> - * The value of this field is indeterminate if the logical display is presented on - * more than one physical display. - * </p> + * The active display mode. */ - public float refreshRate; + public int modeId; /** - * The supported refresh rates of this display at the current resolution in frames per second. + * The default display mode. */ - public float[] supportedRefreshRates = EmptyArray.FLOAT; + public int defaultModeId; + + /** + * The supported modes of this display. + */ + public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY; /** * The logical display density which is the basis for density-independent @@ -276,7 +277,8 @@ public final class DisplayInfo implements Parcelable { && overscanRight == other.overscanRight && overscanBottom == other.overscanBottom && rotation == other.rotation - && refreshRate == other.refreshRate + && modeId == other.modeId + && defaultModeId == other.defaultModeId && logicalDensityDpi == other.logicalDensityDpi && physicalXDpi == other.physicalXDpi && physicalYDpi == other.physicalYDpi @@ -312,9 +314,9 @@ public final class DisplayInfo implements Parcelable { overscanRight = other.overscanRight; overscanBottom = other.overscanBottom; rotation = other.rotation; - refreshRate = other.refreshRate; - supportedRefreshRates = Arrays.copyOf( - other.supportedRefreshRates, other.supportedRefreshRates.length); + modeId = other.modeId; + defaultModeId = other.defaultModeId; + supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length); logicalDensityDpi = other.logicalDensityDpi; physicalXDpi = other.physicalXDpi; physicalYDpi = other.physicalYDpi; @@ -344,8 +346,13 @@ public final class DisplayInfo implements Parcelable { overscanRight = source.readInt(); overscanBottom = source.readInt(); rotation = source.readInt(); - refreshRate = source.readFloat(); - supportedRefreshRates = source.createFloatArray(); + modeId = source.readInt(); + defaultModeId = source.readInt(); + int nModes = source.readInt(); + supportedModes = new Display.Mode[nModes]; + for (int i = 0; i < nModes; i++) { + supportedModes[i] = Display.Mode.CREATOR.createFromParcel(source); + } logicalDensityDpi = source.readInt(); physicalXDpi = source.readFloat(); physicalYDpi = source.readFloat(); @@ -377,8 +384,12 @@ public final class DisplayInfo implements Parcelable { dest.writeInt(overscanRight); dest.writeInt(overscanBottom); dest.writeInt(rotation); - dest.writeFloat(refreshRate); - dest.writeFloatArray(supportedRefreshRates); + dest.writeInt(modeId); + dest.writeInt(defaultModeId); + dest.writeInt(supportedModes.length); + for (int i = 0; i < supportedModes.length; i++) { + supportedModes[i].writeToParcel(dest, flags); + } dest.writeInt(logicalDensityDpi); dest.writeFloat(physicalXDpi); dest.writeFloat(physicalYDpi); @@ -395,6 +406,61 @@ public final class DisplayInfo implements Parcelable { return 0; } + public Display.Mode getMode() { + return findMode(modeId); + } + + public Display.Mode getDefaultMode() { + return findMode(defaultModeId); + } + + private Display.Mode findMode(int id) { + for (int i = 0; i < supportedModes.length; i++) { + if (supportedModes[i].getModeId() == id) { + return supportedModes[i]; + } + } + throw new IllegalStateException("Unable to locate mode " + id); + } + + /** + * Returns the id of the "default" mode with the given refresh rate, or {@code 0} if no suitable + * mode could be found. + */ + public int findDefaultModeByRefreshRate(float refreshRate) { + Display.Mode[] modes = supportedModes; + Display.Mode defaultMode = getDefaultMode(); + for (int i = 0; i < modes.length; i++) { + if (modes[i].matches( + defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), refreshRate)) { + return modes[i].getModeId(); + } + } + return 0; + } + + /** + * Returns the list of supported refresh rates in the default mode. + */ + public float[] getDefaultRefreshRates() { + Display.Mode[] modes = supportedModes; + ArraySet<Float> rates = new ArraySet<>(); + Display.Mode defaultMode = getDefaultMode(); + for (int i = 0; i < modes.length; i++) { + Display.Mode mode = modes[i]; + if (mode.getPhysicalWidth() == defaultMode.getPhysicalWidth() + && mode.getPhysicalHeight() == defaultMode.getPhysicalHeight()) { + rates.add(mode.getRefreshRate()); + } + } + float[] result = new float[rates.size()]; + int i = 0; + for (Float rate : rates) { + result[i++] = rate; + } + return result; + } + public void getAppMetrics(DisplayMetrics outMetrics) { getAppMetrics(outMetrics, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null); } @@ -490,10 +556,12 @@ public final class DisplayInfo implements Parcelable { sb.append(smallestNominalAppWidth); sb.append(" x "); sb.append(smallestNominalAppHeight); - sb.append(", "); - sb.append(refreshRate); - sb.append(" fps, supportedRefreshRates "); - sb.append(Arrays.toString(supportedRefreshRates)); + sb.append(", mode "); + sb.append(modeId); + sb.append(", defaultMode "); + sb.append(defaultModeId); + sb.append(", modes "); + sb.append(Arrays.toString(supportedModes)); sb.append(", rotation "); sb.append(rotation); sb.append(", density "); @@ -541,6 +609,9 @@ public final class DisplayInfo implements Parcelable { if ((flags & Display.FLAG_SCALING_DISABLED) != 0) { result.append(", FLAG_SCALING_DISABLED"); } + if ((flags & Display.FLAG_ROUND) != 0) { + result.append(", FLAG_ROUND"); + } return result.toString(); } } diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 6632f39..5e58250 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -260,7 +260,7 @@ public abstract class HardwareRenderer { /** * Gets the current width of the surface. This is the width that the surface - * was last set to in a call to {@link #setup(int, int, Rect)}. + * was last set to in a call to {@link #setup(int, int, View.AttachInfo, Rect)}. * * @return the current width of the surface */ @@ -268,7 +268,7 @@ public abstract class HardwareRenderer { /** * Gets the current height of the surface. This is the height that the surface - * was last set to in a call to {@link #setup(int, int, Rect)}. + * was last set to in a call to {@link #setup(int, int, View.AttachInfo, Rect)}. * * @return the current width of the surface */ @@ -373,19 +373,20 @@ public abstract class HardwareRenderer { * * @param width The width of the drawing surface. * @param height The height of the drawing surface. + * @param attachInfo Information about the window. * @param surface The surface to hardware accelerate * @param surfaceInsets The drawing surface insets to apply * * @return true if the surface was initialized, false otherwise. Returning * false might mean that the surface was already initialized. */ - boolean initializeIfNeeded(int width, int height, Surface surface, Rect surfaceInsets) - throws OutOfResourcesException { + boolean initializeIfNeeded(int width, int height, View.AttachInfo attachInfo, + Surface surface, Rect surfaceInsets) throws OutOfResourcesException { if (isRequested()) { // We lost the gl context, so recreate it. if (!isEnabled()) { if (initialize(surface)) { - setup(width, height, surfaceInsets); + setup(width, height, attachInfo, surfaceInsets); return true; } } @@ -398,9 +399,17 @@ public abstract class HardwareRenderer { * * @param width The width of the drawing surface. * @param height The height of the drawing surface. + * @param attachInfo Information about the window. * @param surfaceInsets The drawing surface insets to apply */ - abstract void setup(int width, int height, Rect surfaceInsets); + abstract void setup(int width, int height, View.AttachInfo attachInfo, Rect surfaceInsets); + + /** + * Updates the light position based on the position of the window. + * + * @param attachInfo Information about the window. + */ + abstract void setLightCenter(View.AttachInfo attachInfo); /** * Optional, sets the name of the renderer. Useful for debugging purposes. diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 9fc80fc..0fefdc7 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -80,11 +80,17 @@ oneway interface IWindow { int localValue, int localChanges); /** - * If the window manager returned RELAYOUT_RES_ANIMATING - * from relayout(), this method will be called when the animation - * is done. + * The window is beginning to animate. The application should stop drawing frames until the + * window is not animating anymore, indicated by being called {@link #windowEndAnimating}. + * + * @param remainingFrameCount how many frames the app might still draw before stopping drawing */ - void doneAnimating(); + void onAnimationStarted(int remainingFrameCount); + + /** + * The window has ended animating. See {@link #onAnimationStarted}. + */ + void onAnimationStopped(); /** * Called for non-application windows when the enter animation has completed. diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java index c5e4c21..46ef379 100644 --- a/core/java/android/view/InputEventConsistencyVerifier.java +++ b/core/java/android/view/InputEventConsistencyVerifier.java @@ -97,6 +97,9 @@ public final class InputEventConsistencyVerifier { // Set to true if we received hover enter. private boolean mHoverEntered; + // The bitset of buttons which we've received ACTION_BUTTON_PRESS for. + private int mButtonsPressed; + // The current violation message. private StringBuilder mViolationMessage; @@ -148,6 +151,7 @@ public final class InputEventConsistencyVerifier { mTouchEventStreamIsTainted = false; mTouchEventStreamUnhandled = false; mHoverEntered = false; + mButtonsPressed = 0; while (mKeyStateList != null) { final KeyState state = mKeyStateList; @@ -466,6 +470,8 @@ public final class InputEventConsistencyVerifier { final int action = event.getAction(); final int source = event.getSource(); + final int buttonState = event.getButtonState(); + final int actionButton = event.getActionButton(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { switch (action) { case MotionEvent.ACTION_HOVER_ENTER: @@ -486,6 +492,62 @@ public final class InputEventConsistencyVerifier { ensureHistorySizeIsZeroForThisAction(event); ensurePointerCountIsOneForThisAction(event); break; + case MotionEvent.ACTION_BUTTON_PRESS: + ensureActionButtonIsNonZeroForThisAction(event); + if ((mButtonsPressed & actionButton) != 0) { + problem("Action button for ACTION_BUTTON_PRESS event is " + + actionButton + ", but it has already been pressed and " + + "has yet to be released."); + } + + mButtonsPressed |= actionButton; + // The system will automatically mirror the stylus buttons onto the button + // state as the old set of generic buttons for apps targeting pre-M. If + // it looks this has happened, go ahead and set the generic buttons as + // pressed to prevent spurious errors. + if (actionButton == MotionEvent.BUTTON_STYLUS_PRIMARY && + (buttonState & MotionEvent.BUTTON_SECONDARY) != 0) { + mButtonsPressed |= MotionEvent.BUTTON_SECONDARY; + } else if (actionButton == MotionEvent.BUTTON_STYLUS_SECONDARY && + (buttonState & MotionEvent.BUTTON_TERTIARY) != 0) { + mButtonsPressed |= MotionEvent.BUTTON_TERTIARY; + } + + if (mButtonsPressed != buttonState) { + problem(String.format("Reported button state differs from " + + "expected button state based on press and release events. " + + "Is 0x%08x but expected 0x%08x.", + buttonState, mButtonsPressed)); + } + break; + case MotionEvent.ACTION_BUTTON_RELEASE: + ensureActionButtonIsNonZeroForThisAction(event); + if ((mButtonsPressed & actionButton) != actionButton) { + problem("Action button for ACTION_BUTTON_RELEASE event is " + + actionButton + ", but it was either never pressed or has " + + "already been released."); + } + + mButtonsPressed &= ~actionButton; + // The system will automatically mirror the stylus buttons onto the button + // state as the old set of generic buttons for apps targeting pre-M. If + // it looks this has happened, go ahead and set the generic buttons as + // released to prevent spurious errors. + if (actionButton == MotionEvent.BUTTON_STYLUS_PRIMARY && + (buttonState & MotionEvent.BUTTON_SECONDARY) == 0) { + mButtonsPressed &= ~MotionEvent.BUTTON_SECONDARY; + } else if (actionButton == MotionEvent.BUTTON_STYLUS_SECONDARY && + (buttonState & MotionEvent.BUTTON_TERTIARY) == 0) { + mButtonsPressed &= ~MotionEvent.BUTTON_TERTIARY; + } + + if (mButtonsPressed != buttonState) { + problem(String.format("Reported button state differs from " + + "expected button state based on press and release events. " + + "Is 0x%08x but expected 0x%08x.", + buttonState, mButtonsPressed)); + } + break; default: problem("Invalid action for generic pointer event."); break; @@ -563,6 +625,15 @@ public final class InputEventConsistencyVerifier { } } + private void ensureActionButtonIsNonZeroForThisAction(MotionEvent event) { + final int actionButton = event.getActionButton(); + if (actionButton == 0) { + problem("No action button set. Action button should always be non-zero for " + + MotionEvent.actionToString(event.getAction())); + + } + } + private void ensureHistorySizeIsZeroForThisAction(MotionEvent event) { final int historySize = event.getHistorySize(); if (historySize != 0) { diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 1ac3f45..f6ce353 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -1817,9 +1817,7 @@ public class KeyEvent extends InputEvent implements Parcelable { public static final boolean isWakeKey(int keyCode) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: - case KeyEvent.KEYCODE_POWER: case KeyEvent.KEYCODE_MENU: - case KeyEvent.KEYCODE_SLEEP: case KeyEvent.KEYCODE_WAKEUP: case KeyEvent.KEYCODE_PAIRING: return true; diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 5df596a..4394cd8 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -303,6 +303,32 @@ public final class MotionEvent extends InputEvent implements Parcelable { public static final int ACTION_HOVER_EXIT = 10; /** + * Constant for {@link #getActionMasked}: A button has been pressed. + * + * <p> + * Use {@link #getActionButton()} to get which button was pressed. + * </p><p> + * This action is not a touch event so it is delivered to + * {@link View#onGenericMotionEvent(MotionEvent)} rather than + * {@link View#onTouchEvent(MotionEvent)}. + * </p> + */ + public static final int ACTION_BUTTON_PRESS = 11; + + /** + * Constant for {@link #getActionMasked}: A button has been released. + * + * <p> + * Use {@link #getActionButton()} to get which button was released. + * </p><p> + * This action is not a touch event so it is delivered to + * {@link View#onGenericMotionEvent(MotionEvent)} rather than + * {@link View#onTouchEvent(MotionEvent)}. + * </p> + */ + public static final int ACTION_BUTTON_RELEASE = 12; + + /** * Bits in the action code that represent a pointer index, used with * {@link #ACTION_POINTER_DOWN} and {@link #ACTION_POINTER_UP}. Shifting * down by {@link #ACTION_POINTER_INDEX_SHIFT} provides the actual pointer @@ -1174,14 +1200,14 @@ public final class MotionEvent extends InputEvent implements Parcelable { public static final int BUTTON_PRIMARY = 1 << 0; /** - * Button constant: Secondary button (right mouse button, stylus first button). + * Button constant: Secondary button (right mouse button). * * @see #getButtonState */ public static final int BUTTON_SECONDARY = 1 << 1; /** - * Button constant: Tertiary button (middle mouse button, stylus second button). + * Button constant: Tertiary button (middle mouse button). * * @see #getButtonState */ @@ -1209,6 +1235,20 @@ public final class MotionEvent extends InputEvent implements Parcelable { */ public static final int BUTTON_FORWARD = 1 << 4; + /** + * Button constant: Primary stylus button pressed. + * + * @see #getButtonState + */ + public static final int BUTTON_STYLUS_PRIMARY = 1 << 5; + + /** + * Button constant: Secondary stylus button pressed. + * + * @see #getButtonState + */ + public static final int BUTTON_STYLUS_SECONDARY = 1 << 6; + // NOTE: If you add a new axis here you must also add it to: // native/include/android/input.h @@ -1220,8 +1260,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { "BUTTON_TERTIARY", "BUTTON_BACK", "BUTTON_FORWARD", - "0x00000020", - "0x00000040", + "BUTTON_STYLUS_PRIMARY", + "BUTTON_STYLUS_SECONDARY", "0x00000080", "0x00000100", "0x00000200", @@ -1357,6 +1397,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { private static native void nativeSetEdgeFlags(long nativePtr, int action); private static native int nativeGetMetaState(long nativePtr); private static native int nativeGetButtonState(long nativePtr); + private static native void nativeSetButtonState(long nativePtr, int buttonState); + private static native int nativeGetActionButton(long nativePtr); private static native void nativeOffsetLocation(long nativePtr, float deltaX, float deltaY); private static native float nativeGetXOffset(long nativePtr); private static native float nativeGetYOffset(long nativePtr); @@ -2212,12 +2254,36 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @see #BUTTON_TERTIARY * @see #BUTTON_FORWARD * @see #BUTTON_BACK + * @see #BUTTON_STYLUS_PRIMARY + * @see #BUTTON_STYLUS_SECONDARY */ public final int getButtonState() { return nativeGetButtonState(mNativePtr); } /** + * Sets the bitfield indicating which buttons are pressed. + * + * @see #getButtonState() + * @hide + */ + public final void setButtonState(int buttonState) { + nativeSetButtonState(mNativePtr, buttonState); + } + + /** + * Gets which button has been modified during a press or release action. + * + * For actions other than {@link #ACTION_BUTTON_PRESS} and {@link #ACTION_BUTTON_RELEASE} + * the returned value is undefined. + * + * @see #getButtonState() + */ + public final int getActionButton() { + return nativeGetActionButton(mNativePtr); + } + + /** * Returns the original raw X coordinate of this event. For touch * events on the screen, this is the original location of the event * on the screen, before it had been adjusted for the containing window @@ -3013,6 +3079,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { public String toString() { StringBuilder msg = new StringBuilder(); msg.append("MotionEvent { action=").append(actionToString(getAction())); + msg.append(", actionButton=").append(buttonStateToString(getActionButton())); final int pointerCount = getPointerCount(); for (int i = 0; i < pointerCount; i++) { @@ -3066,6 +3133,10 @@ public final class MotionEvent extends InputEvent implements Parcelable { return "ACTION_HOVER_ENTER"; case ACTION_HOVER_EXIT: return "ACTION_HOVER_EXIT"; + case ACTION_BUTTON_PRESS: + return "ACTION_BUTTON_PRESS"; + case ACTION_BUTTON_RELEASE: + return "ACTION_BUTTON_RELEASE"; } int index = (action & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT; switch (action & ACTION_MASK) { @@ -3172,6 +3243,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { * @see #BUTTON_TERTIARY * @see #BUTTON_FORWARD * @see #BUTTON_BACK + * @see #BUTTON_STYLUS_PRIMARY + * @see #BUTTON_STYLUS_SECONDARY */ public final boolean isButtonPressed(int button) { if (button == 0) { @@ -3180,18 +3253,6 @@ public final class MotionEvent extends InputEvent implements Parcelable { return (getButtonState() & button) == button; } - /** - * Checks if a stylus is being used and if the first stylus button is - * pressed. - * - * @return True if the tool is a stylus and if the first stylus button is - * pressed. - * @see #BUTTON_SECONDARY - */ - public final boolean isStylusButtonPressed() { - return (isButtonPressed(BUTTON_SECONDARY) && getToolType(0) == TOOL_TYPE_STYLUS); - } - public static final Parcelable.Creator<MotionEvent> CREATOR = new Parcelable.Creator<MotionEvent>() { public MotionEvent createFromParcel(Parcel in) { diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java index 85b22fb..d70712a 100644 --- a/core/java/android/view/TextureView.java +++ b/core/java/android/view/TextureView.java @@ -717,6 +717,14 @@ public class TextureView extends View { if (surfaceTexture == null) { throw new NullPointerException("surfaceTexture must not be null"); } + if (surfaceTexture == mSurface) { + throw new IllegalArgumentException("Trying to setSurfaceTexture to " + + "the same SurfaceTexture that's already set."); + } + if (surfaceTexture.isReleased()) { + throw new IllegalArgumentException("Cannot setSurfaceTexture to a " + + "released SurfaceTexture"); + } if (mSurface != null) { mSurface.release(); } diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 1fd7109..7f243d3 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -19,11 +19,10 @@ package android.view; import android.annotation.IntDef; import android.annotation.NonNull; import android.content.Context; -import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; +import android.graphics.Point; import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.os.Binder; import android.os.IBinder; import android.os.ParcelFileDescriptor; @@ -31,7 +30,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.Trace; import android.util.Log; -import android.util.LongSparseArray; import android.view.Surface.OutOfResourcesException; import android.view.View.AttachInfo; @@ -41,8 +39,6 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.HashSet; /** * Hardware renderer that proxies the rendering to a render thread. Most calls @@ -197,10 +193,10 @@ public class ThreadedRenderer extends HardwareRenderer { } @Override - void setup(int width, int height, Rect surfaceInsets) { - final float lightX = width / 2.0f; + void setup(int width, int height, AttachInfo attachInfo, Rect surfaceInsets) { mWidth = width; mHeight = height; + if (surfaceInsets != null && (surfaceInsets.left != 0 || surfaceInsets.right != 0 || surfaceInsets.top != 0 || surfaceInsets.bottom != 0)) { mHasInsets = true; @@ -218,10 +214,23 @@ public class ThreadedRenderer extends HardwareRenderer { mSurfaceWidth = width; mSurfaceHeight = height; } + mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight); - nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight, - lightX, mLightY, mLightZ, mLightRadius, + nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight, mLightRadius, mAmbientShadowAlpha, mSpotShadowAlpha); + + setLightCenter(attachInfo); + } + + @Override + void setLightCenter(AttachInfo attachInfo) { + // Adjust light position for window offsets. + final Point displaySize = attachInfo.mPoint; + attachInfo.mDisplay.getRealSize(displaySize); + final float lightX = displaySize.x / 2f - attachInfo.mWindowLeft; + final float lightY = mLightY - attachInfo.mWindowTop; + + nSetLightCenter(mNativeProxy, lightX, lightY, mLightZ); } @Override @@ -500,8 +509,9 @@ public class ThreadedRenderer extends HardwareRenderer { private static native void nUpdateSurface(long nativeProxy, Surface window); private static native boolean nPauseSurface(long nativeProxy, Surface window); private static native void nSetup(long nativeProxy, int width, int height, - float lightX, float lightY, float lightZ, float lightRadius, - int ambientShadowAlpha, int spotShadowAlpha); + float lightRadius, int ambientShadowAlpha, int spotShadowAlpha); + private static native void nSetLightCenter(long nativeProxy, + float lightX, float lightY, float lightZ); private static native void nSetOpaque(long nativeProxy, boolean opaque); private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size); private static native void nDestroy(long nativeProxy); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index e1f1816..6ca320a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -4265,12 +4265,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } mForegroundInfo.mInsidePadding = a.getBoolean(attr, mForegroundInfo.mInsidePadding); + break; case R.styleable.View_scrollIndicators: final int scrollIndicators = - a.getInt(attr, SCROLL_INDICATORS_NONE) & SCROLL_INDICATORS_PFLAG3_MASK; + (a.getInt(attr, 0) << SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT) + & SCROLL_INDICATORS_PFLAG3_MASK; if (scrollIndicators != 0) { - viewFlagValues |= scrollIndicators; - viewFlagMasks |= SCROLL_INDICATORS_PFLAG3_MASK; + mPrivateFlags3 |= scrollIndicators; initializeScrollIndicators = true; } break; @@ -4884,7 +4885,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @attr ref android.R.styleable#View_scrollIndicators */ public void setScrollIndicators(@ScrollIndicators int indicators) { - setScrollIndicators(indicators, SCROLL_INDICATORS_PFLAG3_MASK); + setScrollIndicators(indicators, + SCROLL_INDICATORS_PFLAG3_MASK >>> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT); } /** @@ -4954,36 +4956,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, >>> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; } - /** - * Returns whether the specified scroll indicator is enabled. - * <p> - * Multiple indicator types may be queried by passing the logical OR of the - * desired types. If multiple types are specified, the return value - * represents whether they are all enabled. - * - * @param direction the indicator direction, or the logical OR of multiple - * indicator directions. One or more of: - * <ul> - * <li>{@link #SCROLL_INDICATOR_TOP}</li> - * <li>{@link #SCROLL_INDICATOR_BOTTOM}</li> - * <li>{@link #SCROLL_INDICATOR_LEFT}</li> - * <li>{@link #SCROLL_INDICATOR_RIGHT}</li> - * <li>{@link #SCROLL_INDICATOR_START}</li> - * <li>{@link #SCROLL_INDICATOR_END}</li> - * </ul> - * @return {@code true} if the specified indicator(s) are enabled, - * {@code false} otherwise - * @attr ref android.R.styleable#View_scrollIndicators - */ - public boolean isScrollIndicatorEnabled(int direction) { - // Shift and sanitize input. - direction <<= SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT; - direction &= SCROLL_INDICATORS_PFLAG3_MASK; - - // All of the flags must be set. - return (mPrivateFlags3 & direction) == direction; - } - ListenerInfo getListenerInfo() { if (mListenerInfo != null) { return mListenerInfo; @@ -5249,8 +5221,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return True if the event was consumed. */ private boolean performStylusActionOnButtonPress(MotionEvent event) { - if (isStylusButtonPressable() && !mInStylusButtonPress - && !mHasPerformedLongPress && event.isStylusButtonPressed()) { + if (isStylusButtonPressable() && !mInStylusButtonPress && !mHasPerformedLongPress + && event.isButtonPressed(MotionEvent.BUTTON_STYLUS_SECONDARY)) { if (performStylusButtonPress()) { mInStylusButtonPress = true; setPressed(true, event.getX(), event.getY()); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 51c4760..b476e9b 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -586,6 +586,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager mGroupFlags |= FLAG_CLIP_CHILDREN; mGroupFlags |= FLAG_CLIP_TO_PADDING; mGroupFlags |= FLAG_ANIMATION_DONE; + mGroupFlags |= FLAG_ANIMATION_CACHE; + mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE; if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) { mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 57c6cbf..b7d902c 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -40,6 +40,7 @@ import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.media.AudioManager; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.Handler; @@ -220,6 +221,9 @@ public final class ViewRootImpl implements ViewParent, boolean mLastWasImTarget; boolean mWindowsAnimating; boolean mDrawDuringWindowsAnimating; + + /** How many frames the app is still allowed to draw when a window animation is happening. */ + private int mRemainingFrameCount; boolean mIsDrawing; int mLastSystemUiVisibility; int mClientWindowLayoutFlags; @@ -962,6 +966,12 @@ public final class ViewRootImpl implements ViewParent, } } + invalidateRectOnScreen(dirty); + + return null; + } + + private void invalidateRectOnScreen(Rect dirty) { final Rect localDirty = mDirty; if (!localDirty.isEmpty() && !localDirty.contains(dirty)) { mAttachInfo.mSetIgnoreDirtyState = true; @@ -981,8 +991,6 @@ public final class ViewRootImpl implements ViewParent, if (!mWillDrawSoon && (intersected || mIsAnimating)) { scheduleTraversals(); } - - return null; } void setWindowStopped(boolean stopped) { @@ -1569,10 +1577,6 @@ public final class ViewRootImpl implements ViewParent, } final int surfaceGenerationId = mSurface.getGenerationId(); relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); - if (!mDrawDuringWindowsAnimating && - (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0) { - mWindowsAnimating = true; - } if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString() + " overscan=" + mPendingOverscanInsets.toShortString() @@ -1813,15 +1817,15 @@ public final class ViewRootImpl implements ViewParent, } } - if (mAttachInfo.mHardwareRenderer != null && - mAttachInfo.mHardwareRenderer.isEnabled()) { - if (hwInitialized || - mWidth != mAttachInfo.mHardwareRenderer.getWidth() || - mHeight != mAttachInfo.mHardwareRenderer.getHeight()) { - mAttachInfo.mHardwareRenderer.setup( - mWidth, mHeight, mWindowAttributes.surfaceInsets); + final HardwareRenderer hardwareRenderer = mAttachInfo.mHardwareRenderer; + if (hardwareRenderer != null && hardwareRenderer.isEnabled()) { + if (hwInitialized + || mWidth != hardwareRenderer.getWidth() + || mHeight != hardwareRenderer.getHeight()) { + hardwareRenderer.setup(mWidth, mHeight, mAttachInfo, + mWindowAttributes.surfaceInsets); if (!hwInitialized) { - mAttachInfo.mHardwareRenderer.invalidate(mSurface); + hardwareRenderer.invalidate(mSurface); mFullRedrawNeeded = true; } } @@ -1897,6 +1901,11 @@ public final class ViewRootImpl implements ViewParent, } mAttachInfo.mWindowLeft = frame.left; mAttachInfo.mWindowTop = frame.top; + + // Update the light position for the new window offsets. + if (mAttachInfo.mHardwareRenderer != null) { + mAttachInfo.mHardwareRenderer.setLightCenter(mAttachInfo); + } } } @@ -1996,14 +2005,11 @@ public final class ViewRootImpl implements ViewParent, + mView.findFocus()); } } - if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0) { - // The first time we relayout the window, if the system is - // doing window animations, we want to hold of on any future - // draws until the animation is done. - mWindowsAnimating = true; - } } else if (mWindowsAnimating) { - skipDraw = true; + if (mRemainingFrameCount <= 0) { + skipDraw = true; + } + mRemainingFrameCount--; } mFirst = false; @@ -2608,7 +2614,7 @@ public final class ViewRootImpl implements ViewParent, try { mAttachInfo.mHardwareRenderer.initializeIfNeeded( - mWidth, mHeight, mSurface, surfaceInsets); + mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets); } catch (OutOfResourcesException e) { handleOutOfResourcesException(e); return; @@ -2800,7 +2806,7 @@ public final class ViewRootImpl implements ViewParent, public void setDrawDuringWindowsAnimating(boolean value) { mDrawDuringWindowsAnimating = value; if (value) { - handleDispatchDoneAnimating(); + handleDispatchWindowAnimationStopped(); } } @@ -3152,11 +3158,12 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_UPDATE_CONFIGURATION = 18; private final static int MSG_PROCESS_INPUT_EVENTS = 19; private final static int MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST = 21; - private final static int MSG_DISPATCH_DONE_ANIMATING = 22; - private final static int MSG_INVALIDATE_WORLD = 23; - private final static int MSG_WINDOW_MOVED = 24; - private final static int MSG_SYNTHESIZE_INPUT_EVENT = 25; - private final static int MSG_DISPATCH_WINDOW_SHOWN = 26; + private final static int MSG_INVALIDATE_WORLD = 22; + private final static int MSG_WINDOW_MOVED = 23; + private final static int MSG_SYNTHESIZE_INPUT_EVENT = 24; + private final static int MSG_DISPATCH_WINDOW_SHOWN = 25; + private final static int MSG_DISPATCH_WINDOW_ANIMATION_STOPPED = 26; + private final static int MSG_DISPATCH_WINDOW_ANIMATION_STARTED = 27; final class ViewRootHandler extends Handler { @Override @@ -3200,8 +3207,10 @@ public final class ViewRootImpl implements ViewParent, return "MSG_PROCESS_INPUT_EVENTS"; case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: return "MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST"; - case MSG_DISPATCH_DONE_ANIMATING: - return "MSG_DISPATCH_DONE_ANIMATING"; + case MSG_DISPATCH_WINDOW_ANIMATION_STARTED: + return "MSG_DISPATCH_WINDOW_ANIMATION_STARTED"; + case MSG_DISPATCH_WINDOW_ANIMATION_STOPPED: + return "MSG_DISPATCH_WINDOW_ANIMATION_STOPPED"; case MSG_WINDOW_MOVED: return "MSG_WINDOW_MOVED"; case MSG_SYNTHESIZE_INPUT_EVENT: @@ -3300,7 +3309,7 @@ public final class ViewRootImpl implements ViewParent, final WindowManager.LayoutParams lp = mWindowAttributes; final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null; mAttachInfo.mHardwareRenderer.initializeIfNeeded( - mWidth, mHeight, mSurface, surfaceInsets); + mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets); } catch (OutOfResourcesException e) { Log.e(TAG, "OutOfResourcesException locking surface", e); try { @@ -3421,8 +3430,12 @@ public final class ViewRootImpl implements ViewParent, case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: { setAccessibilityFocus(null, null); } break; - case MSG_DISPATCH_DONE_ANIMATING: { - handleDispatchDoneAnimating(); + case MSG_DISPATCH_WINDOW_ANIMATION_STARTED: { + int remainingFrameCount = msg.arg1; + handleDispatchWindowAnimationStarted(remainingFrameCount); + } break; + case MSG_DISPATCH_WINDOW_ANIMATION_STOPPED: { + handleDispatchWindowAnimationStopped(); } break; case MSG_INVALIDATE_WORLD: { if (mView != null) { @@ -4038,7 +4051,7 @@ public final class ViewRootImpl implements ViewParent, } else { // If delivering a new non-key event, make sure the window is // now allowed to start updating. - handleDispatchDoneAnimating(); + handleDispatchWindowAnimationStopped(); final int source = q.mEvent.getSource(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { return processPointerEvent(q); @@ -4068,7 +4081,7 @@ public final class ViewRootImpl implements ViewParent, if (event.getAction() != KeyEvent.ACTION_UP) { // If delivering a new key event, make sure the window is // now allowed to start updating. - handleDispatchDoneAnimating(); + handleDispatchWindowAnimationStopped(); } // Deliver the key to the view hierarchy. @@ -5285,7 +5298,14 @@ public final class ViewRootImpl implements ViewParent, } } - public void handleDispatchDoneAnimating() { + public void handleDispatchWindowAnimationStarted(int remainingFrameCount) { + if (!mDrawDuringWindowsAnimating) { + mRemainingFrameCount = remainingFrameCount; + mWindowsAnimating = true; + } + } + + public void handleDispatchWindowAnimationStopped() { if (mWindowsAnimating) { mWindowsAnimating = false; if (!mDirty.isEmpty() || mIsAnimating || mFullRedrawNeeded) { @@ -5347,7 +5367,7 @@ public final class ViewRootImpl implements ViewParent, //Log.d(TAG, ">>>>>> CALLING relayout"); if (params != null && mOrigWindowType != params.type) { // For compatibility with old apps, don't crash here. - if (mTargetSdkVersion < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { Slog.w(TAG, "Window type can not be changed after " + "the window is added; ignoring change of " + mView); params.type = mOrigWindowType; @@ -5772,6 +5792,7 @@ public final class ViewRootImpl implements ViewParent, void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { + adjustInputEventForCompatibility(event); QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags); // Always enqueue the input event in order, regardless of its time stamp. @@ -5877,6 +5898,19 @@ public final class ViewRootImpl implements ViewParent, recycleQueuedInputEvent(q); } + private void adjustInputEventForCompatibility(InputEvent e) { + if (mTargetSdkVersion < Build.VERSION_CODES.MNC && e instanceof MotionEvent) { + MotionEvent motion = (MotionEvent) e; + final int mask = + MotionEvent.BUTTON_STYLUS_PRIMARY | MotionEvent.BUTTON_STYLUS_SECONDARY; + final int buttonState = motion.getButtonState(); + final int compatButtonState = (buttonState & mask) >> 4; + if (compatButtonState != 0) { + motion.setButtonState(buttonState | compatButtonState); + } + } + } + static boolean isTerminalInputEvent(InputEvent event) { if (event instanceof KeyEvent) { final KeyEvent keyEvent = (KeyEvent)event; @@ -6189,8 +6223,13 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args)); } - public void dispatchDoneAnimating() { - mHandler.sendEmptyMessage(MSG_DISPATCH_DONE_ANIMATING); + public void dispatchWindowAnimationStarted(int remainingFrameCount) { + mHandler.obtainMessage(MSG_DISPATCH_WINDOW_ANIMATION_STARTED, + remainingFrameCount, 0 /* unused */).sendToTarget(); + } + + public void dispatchWindowAnimationStopped() { + mHandler.sendEmptyMessage(MSG_DISPATCH_WINDOW_ANIMATION_STOPPED); } public void dispatchCheckFocus() { @@ -6363,7 +6402,14 @@ public final class ViewRootImpl implements ViewParent, } // Refresh the node for the focused virtual view. + final Rect oldBounds = mTempRect; + mAccessibilityFocusedVirtualView.getBoundsInScreen(oldBounds); mAccessibilityFocusedVirtualView = provider.createAccessibilityNodeInfo(focusedChildId); + final Rect newBounds = mAccessibilityFocusedVirtualView.getBoundsInScreen(); + if (!oldBounds.equals(newBounds)) { + oldBounds.union(newBounds); + invalidateRectOnScreen(oldBounds); + } } @Override @@ -6713,10 +6759,18 @@ public final class ViewRootImpl implements ViewParent, } @Override - public void doneAnimating() { + public void onAnimationStarted(int remainingFrameCount) { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchWindowAnimationStarted(remainingFrameCount); + } + } + + @Override + public void onAnimationStopped() { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { - viewAncestor.dispatchDoneAnimating(); + viewAncestor.dispatchWindowAnimationStopped(); } } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 49a72ce..07984e9 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -1784,14 +1784,6 @@ public abstract class Window { public void setAllowReturnTransitionOverlap(boolean allow) {} /** - * TODO: remove this. - * @hide - */ - public void setAllowExitTransitionOverlap(boolean allow) { - setAllowReturnTransitionOverlap(allow); - } - - /** * Returns how the transition set in * {@link #setExitTransition(android.transition.Transition)} overlaps with the exit * transition of the called Activity when reentering after if finishes. When true, @@ -1805,12 +1797,6 @@ public abstract class Window { public boolean getAllowReturnTransitionOverlap() { return true; } /** - * TODO: remove this. - * @hide - */ - public boolean getAllowExitTransitionOverlap() { return getAllowReturnTransitionOverlap(); } - - /** * Returns the duration, in milliseconds, of the window background fade * when transitioning into or away from an Activity when called with an Activity Transition. * <p>When executing the enter transition, the background starts transparent diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 2797b6e..7976ca4 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -101,7 +101,7 @@ public interface WindowManager extends ViewManager { * the given view hierarchy's {@link View#onDetachedFromWindow() * View.onDetachedFromWindow()} methods before returning. This is not * for normal applications; using it correctly requires great care. - * + * * @param view The view to be removed. */ public void removeViewImmediate(View view); @@ -115,7 +115,7 @@ public interface WindowManager extends ViewManager { */ @ViewDebug.ExportedProperty public int x; - + /** * Y position for this window. With the default gravity it is ignored. * When using {@link Gravity#TOP} or {@link Gravity#BOTTOM} it provides @@ -228,12 +228,12 @@ public interface WindowManager extends ViewManager { @ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION_STARTING, to = "TYPE_VOICE_INTERACTION_STARTING"), }) public int type; - + /** * Start of window types that represent normal application windows. */ public static final int FIRST_APPLICATION_WINDOW = 1; - + /** * Window type: an application window that serves as the "base" window * of the overall application; all other application windows will @@ -241,14 +241,14 @@ public interface WindowManager extends ViewManager { * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_BASE_APPLICATION = 1; - + /** * Window type: a normal application window. The {@link #token} must be * an Activity token identifying who the window belongs to. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_APPLICATION = 2; - + /** * Window type: special application window that is displayed while the * application is starting. Not for use by applications themselves; @@ -257,7 +257,7 @@ public interface WindowManager extends ViewManager { * In multiuser systems shows on all users' windows. */ public static final int TYPE_APPLICATION_STARTING = 3; - + /** * End of types of application windows. */ @@ -330,14 +330,14 @@ public interface WindowManager extends ViewManager { * In multiuser systems shows on all users' windows. */ public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW; - + /** * Window type: the search bar. There can be only one search bar * window; it is placed at the top of the screen. * In multiuser systems shows on all users' windows. */ public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1; - + /** * Window type: phone. These are non-application windows providing * user interaction with the phone (in particular incoming calls). @@ -346,7 +346,7 @@ public interface WindowManager extends ViewManager { * In multiuser systems shows on all users' windows. */ public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2; - + /** * Window type: system window, such as low power alert. These windows * are always on top of application windows. @@ -366,7 +366,7 @@ public interface WindowManager extends ViewManager { * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5; - + /** * Window type: system overlay windows, which need to be displayed * on top of everything else. These windows must not take input @@ -374,7 +374,7 @@ public interface WindowManager extends ViewManager { * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6; - + /** * Window type: priority phone UI, which needs to be displayed even if * the keyguard is active. These windows must not take input @@ -382,26 +382,26 @@ public interface WindowManager extends ViewManager { * In multiuser systems shows on all users' windows. */ public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7; - + /** * Window type: panel that slides out from the status bar * In multiuser systems shows on all users' windows. */ public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8; - + /** * Window type: dialogs that the keyguard shows * In multiuser systems shows on all users' windows. */ public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9; - + /** * Window type: internal system error windows, appear on top of * everything they can. * In multiuser systems shows only on the owning user's window. */ public static final int TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10; - + /** * Window type: internal input methods windows, which appear above * the normal UI. Application windows may be resized or panned to keep @@ -581,16 +581,16 @@ public interface WindowManager extends ViewManager { /** @deprecated this is ignored, this value is set automatically when needed. */ @Deprecated public static final int MEMORY_TYPE_PUSH_BUFFERS = 3; - + /** * @deprecated this is ignored */ @Deprecated public int memoryType; - + /** Window flag: as long as this window is visible to the user, allow - * the lock screen to activate while the screen is on. - * This can be used independently, or in combination with + * the lock screen to activate while the screen is on. + * This can be used independently, or in combination with * {@link #FLAG_KEEP_SCREEN_ON} and/or {@link #FLAG_SHOW_WHEN_LOCKED} */ public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001; @@ -608,26 +608,26 @@ public interface WindowManager extends ViewManager { * instead go to whatever focusable window is behind it. This flag * will also enable {@link #FLAG_NOT_TOUCH_MODAL} whether or not that * is explicitly set. - * + * * <p>Setting this flag also implies that the window will not need to * interact with - * a soft input method, so it will be Z-ordered and positioned + * a soft input method, so it will be Z-ordered and positioned * independently of any active input method (typically this means it * gets Z-ordered on top of the input method, so it can use the full * screen for its content and cover the input method if needed. You * can use {@link #FLAG_ALT_FOCUSABLE_IM} to modify this behavior. */ public static final int FLAG_NOT_FOCUSABLE = 0x00000008; - + /** Window flag: this window can never receive touch events. */ public static final int FLAG_NOT_TOUCHABLE = 0x00000010; - + /** Window flag: even when this window is focusable (its * {@link #FLAG_NOT_FOCUSABLE} is not set), allow any pointer events * outside of the window to be sent to the windows behind it. Otherwise * it will consume all pointer events itself, regardless of whether they * are inside of the window. */ public static final int FLAG_NOT_TOUCH_MODAL = 0x00000020; - + /** Window flag: when set, if the device is asleep when the touch * screen is pressed, you will receive this first touch event. Usually * the first touch event is consumed by the system since the user can @@ -637,21 +637,21 @@ public interface WindowManager extends ViewManager { */ @Deprecated public static final int FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040; - + /** Window flag: as long as this window is visible to the user, keep * the device's screen turned on and bright. */ public static final int FLAG_KEEP_SCREEN_ON = 0x00000080; - + /** Window flag: place the window within the entire screen, ignoring * decorations around the border (such as the status bar). The * window must correctly position its contents to take the screen * decoration into account. This flag is normally set for you * by Window as described in {@link Window#setFlags}. */ public static final int FLAG_LAYOUT_IN_SCREEN = 0x00000100; - + /** Window flag: allow window to extend outside of the screen. */ public static final int FLAG_LAYOUT_NO_LIMITS = 0x00000200; - + /** * Window flag: hide all screen decorations (such as the status bar) while * this window is displayed. This allows the window to use the entire @@ -673,17 +673,17 @@ public interface WindowManager extends ViewManager { * {@link android.R.style#Theme_DeviceDefault_Light_NoActionBar_Fullscreen}.</p> */ public static final int FLAG_FULLSCREEN = 0x00000400; - + /** Window flag: override {@link #FLAG_FULLSCREEN} and force the * screen decorations (such as the status bar) to be shown. */ public static final int FLAG_FORCE_NOT_FULLSCREEN = 0x00000800; - + /** Window flag: turn on dithering when compositing this window to * the screen. * @deprecated This flag is no longer used. */ @Deprecated public static final int FLAG_DITHER = 0x00001000; - + /** Window flag: treat the content of the window as secure, preventing * it from appearing in screenshots or from being viewed on non-secure * displays. @@ -692,21 +692,21 @@ public interface WindowManager extends ViewManager { * secure surfaces and secure displays. */ public static final int FLAG_SECURE = 0x00002000; - + /** Window flag: a special mode where the layout parameters are used * to perform scaling of the surface when it is composited to the * screen. */ public static final int FLAG_SCALED = 0x00004000; - + /** Window flag: intended for windows that will often be used when the user is * holding the screen against their face, it will aggressively filter the event * stream to prevent unintended presses in this situation that may not be - * desired for a particular window, when such an event stream is detected, the + * desired for a particular window, when such an event stream is detected, the * application will receive a CANCEL motion event to indicate this so applications - * can handle this accordingly by taking no action on the event + * can handle this accordingly by taking no action on the event * until the finger is released. */ public static final int FLAG_IGNORE_CHEEK_PRESSES = 0x00008000; - + /** Window flag: a special option only for use in combination with * {@link #FLAG_LAYOUT_IN_SCREEN}. When requesting layout in the * screen your window may appear on top of or behind screen decorations @@ -715,7 +715,7 @@ public interface WindowManager extends ViewManager { * content is not covered by screen decorations. This flag is normally * set for you by Window as described in {@link Window#setFlags}.*/ public static final int FLAG_LAYOUT_INSET_DECOR = 0x00010000; - + /** Window flag: invert the state of {@link #FLAG_NOT_FOCUSABLE} with * respect to how this window interacts with the current method. That * is, if FLAG_NOT_FOCUSABLE is set and this flag is set, then the @@ -726,7 +726,7 @@ public interface WindowManager extends ViewManager { * to use more space and cover the input method. */ public static final int FLAG_ALT_FOCUSABLE_IM = 0x00020000; - + /** Window flag: if you have set {@link #FLAG_NOT_TOUCH_MODAL}, you * can set this flag to receive a single special MotionEvent with * the action @@ -736,7 +736,7 @@ public interface WindowManager extends ViewManager { * first down as an ACTION_OUTSIDE. */ public static final int FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000; - + /** Window flag: special flag to let windows be shown when the screen * is locked. This will let application windows take precedence over * key guard or any other lock screens. Can be used with @@ -766,13 +766,13 @@ public interface WindowManager extends ViewManager { * {@link android.R.style#Theme_DeviceDefault_Wallpaper_NoTitleBar}.</p> */ public static final int FLAG_SHOW_WALLPAPER = 0x00100000; - + /** Window flag: when set as a window is being added or made * visible, once the window has been shown then the system will * poke the power manager's user activity (as if the user had woken * up the device) to turn the screen on. */ public static final int FLAG_TURN_SCREEN_ON = 0x00200000; - + /** Window flag: when set the window will cause the keyguard to * be dismissed, only if it is not a secure lock keyguard. Because such * a keyguard is not needed for security, it will never re-appear if @@ -786,7 +786,7 @@ public interface WindowManager extends ViewManager { * also been set. */ public static final int FLAG_DISMISS_KEYGUARD = 0x00400000; - + /** Window flag: when set the window will accept for touch events * outside of its bounds to be sent to other windows that also * support split touch. When this flag is not set, the first pointer @@ -798,7 +798,7 @@ public interface WindowManager extends ViewManager { * to be split across multiple windows. */ public static final int FLAG_SPLIT_TOUCH = 0x00800000; - + /** * <p>Indicates whether this window should be hardware accelerated. * Requesting hardware acceleration does not guarantee it will happen.</p> @@ -940,7 +940,7 @@ public interface WindowManager extends ViewManager { /** * Various behavioral options/flags. Default is none. - * + * * @see #FLAG_ALLOW_LOCK_WHILE_SCREEN_ON * @see #FLAG_DIM_BEHIND * @see #FLAG_NOT_FOCUSABLE @@ -1041,10 +1041,10 @@ public interface WindowManager extends ViewManager { * as if it was. * Like {@link #FLAG_HARDWARE_ACCELERATED} except for trusted system windows * that need hardware acceleration (e.g. LockScreen), where hardware acceleration - * is generally disabled. This flag must be specified in addition to + * is generally disabled. This flag must be specified in addition to * {@link #FLAG_HARDWARE_ACCELERATED} to enable hardware acceleration for system * windows. - * + * * @hide */ public static final int PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED = 0x00000001; @@ -1055,7 +1055,7 @@ public interface WindowManager extends ViewManager { * If certain parts of the UI that really do want to use hardware * acceleration, this flag can be set to force it. This is basically * for the lock screen. Anyone else using it, you are probably wrong. - * + * * @hide */ public static final int PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED = 0x00000002; @@ -1194,63 +1194,63 @@ public interface WindowManager extends ViewManager { } return false; } - + /** * Mask for {@link #softInputMode} of the bits that determine the * desired visibility state of the soft input area for this window. */ public static final int SOFT_INPUT_MASK_STATE = 0x0f; - + /** * Visibility state for {@link #softInputMode}: no state has been specified. */ public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0; - + /** * Visibility state for {@link #softInputMode}: please don't change the state of * the soft input area. */ public static final int SOFT_INPUT_STATE_UNCHANGED = 1; - + /** * Visibility state for {@link #softInputMode}: please hide any soft input * area when normally appropriate (when the user is navigating * forward to your window). */ public static final int SOFT_INPUT_STATE_HIDDEN = 2; - + /** * Visibility state for {@link #softInputMode}: please always hide any * soft input area when this window receives focus. */ public static final int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3; - + /** * Visibility state for {@link #softInputMode}: please show the soft * input area when normally appropriate (when the user is navigating * forward to your window). */ public static final int SOFT_INPUT_STATE_VISIBLE = 4; - + /** * Visibility state for {@link #softInputMode}: please always make the * soft input area visible when this window receives input focus. */ public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5; - + /** * Mask for {@link #softInputMode} of the bits that determine the * way that the window should be adjusted to accommodate the soft * input window. */ public static final int SOFT_INPUT_MASK_ADJUST = 0xf0; - + /** Adjustment option for {@link #softInputMode}: nothing specified. * The system will try to pick one or * the other depending on the contents of the window. */ public static final int SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00; - + /** Adjustment option for {@link #softInputMode}: set to allow the * window to be resized when an input * method is shown, so that its contents are not covered by the input @@ -1263,7 +1263,7 @@ public interface WindowManager extends ViewManager { * not resize, but will stay fullscreen. */ public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10; - + /** Adjustment option for {@link #softInputMode}: set to have a window * pan when an input method is * shown, so it doesn't need to deal with resizing but just panned @@ -1273,7 +1273,7 @@ public interface WindowManager extends ViewManager { * the other depending on the contents of the window. */ public static final int SOFT_INPUT_ADJUST_PAN = 0x20; - + /** Adjustment option for {@link #softInputMode}: set to have a window * not adjust for a shown input method. The window will not be resized, * and it will not be panned to make its focus visible. @@ -1292,7 +1292,7 @@ public interface WindowManager extends ViewManager { /** * Desired operating mode for any soft input area. May be any combination * of: - * + * * <ul> * <li> One of the visibility states * {@link #SOFT_INPUT_STATE_UNSPECIFIED}, {@link #SOFT_INPUT_STATE_UNCHANGED}, @@ -1309,7 +1309,7 @@ public interface WindowManager extends ViewManager { * {@link android.R.attr#windowSoftInputMode} attribute.</p> */ public int softInputMode; - + /** * Placement of window within the screen as per {@link Gravity}. Both * {@link Gravity#apply(int, int, int, android.graphics.Rect, int, int, @@ -1326,7 +1326,7 @@ public interface WindowManager extends ViewManager { * @see Gravity */ public int gravity; - + /** * The horizontal margin, as a percentage of the container's width, * between the container and the widget. See @@ -1335,7 +1335,7 @@ public interface WindowManager extends ViewManager { * field is added with {@link #x} to supply the <var>xAdj</var> parameter. */ public float horizontalMargin; - + /** * The vertical margin, as a percentage of the container's height, * between the container and the widget. See @@ -1361,26 +1361,26 @@ public interface WindowManager extends ViewManager { * @hide */ public boolean hasManualSurfaceInsets; - + /** * The desired bitmap format. May be one of the constants in * {@link android.graphics.PixelFormat}. Default is OPAQUE. */ public int format; - + /** * A style resource defining the animations to use for this window. * This must be a system resource; it can not be an application resource * because the window manager does not have access to applications. */ public int windowAnimations; - + /** * An alpha value to apply to this entire window. * An alpha of 1.0 means fully opaque and 0.0 means fully transparent */ public float alpha = 1.0f; - + /** * When {@link #FLAG_DIM_BEHIND} is set, this is the amount of dimming * to apply. Range is from 1.0 for completely opaque to 0.0 for no @@ -1408,7 +1408,7 @@ public interface WindowManager extends ViewManager { * to the hightest value when this window is in front. */ public static final float BRIGHTNESS_OVERRIDE_FULL = 1.0f; - + /** * This can be used to override the user's preferred brightness of * the screen. A value of less than 0, the default, means to use the @@ -1416,7 +1416,7 @@ public interface WindowManager extends ViewManager { * dark to full bright. */ public float screenBrightness = BRIGHTNESS_OVERRIDE_NONE; - + /** * This can be used to override the standard behavior of the button and * keyboard backlights. A value of less than 0, the default, means to @@ -1450,7 +1450,7 @@ public interface WindowManager extends ViewManager { * opaque windows have the #FLAG_FULLSCREEN bit set and are not covered * by other windows. All other situations default to the * {@link #ROTATION_ANIMATION_ROTATE} behavior. - * + * * @see #ROTATION_ANIMATION_ROTATE * @see #ROTATION_ANIMATION_CROSSFADE * @see #ROTATION_ANIMATION_JUMPCUT @@ -1462,18 +1462,18 @@ public interface WindowManager extends ViewManager { * you. */ public IBinder token = null; - + /** * Name of the package owning this window. */ public String packageName = null; - + /** * Specific orientation value for a window. * May be any of the same values allowed - * for {@link android.content.pm.ActivityInfo#screenOrientation}. - * If not set, a default value of - * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} + * for {@link android.content.pm.ActivityInfo#screenOrientation}. + * If not set, a default value of + * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} * will be used. */ public int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -1482,13 +1482,28 @@ public interface WindowManager extends ViewManager { * The preferred refresh rate for the window. * * This must be one of the supported refresh rates obtained for the display(s) the window - * is on. + * is on. The selected refresh rate will be applied to the display's default mode. + * + * This value is ignored if {@link #preferredDisplayModeId} is set. * * @see Display#getSupportedRefreshRates() + * @deprecated use {@link #preferredDisplayModeId} instead */ + @Deprecated public float preferredRefreshRate; /** + * Id of the preferred display mode for the window. + * <p> + * This must be one of the supported modes obtained for the display(s) the window is on. + * A value of {@code 0} means no preference. + * + * @see Display#getSupportedModes() + * @see Display.Mode#getModeId() + */ + public int preferredDisplayModeId; + + /** * Control the visibility of the status bar. * * @see View#STATUS_BAR_VISIBLE @@ -1505,7 +1520,7 @@ public interface WindowManager extends ViewManager { /** * Get callbacks about the system ui visibility changing. - * + * * TODO: Maybe there should be a bitfield of optional callbacks that we need. * * @hide @@ -1571,34 +1586,34 @@ public interface WindowManager extends ViewManager { type = TYPE_APPLICATION; format = PixelFormat.OPAQUE; } - + public LayoutParams(int _type) { super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = _type; format = PixelFormat.OPAQUE; } - + public LayoutParams(int _type, int _flags) { super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = _type; flags = _flags; format = PixelFormat.OPAQUE; } - + public LayoutParams(int _type, int _flags, int _format) { super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = _type; flags = _flags; format = _format; } - + public LayoutParams(int w, int h, int _type, int _flags, int _format) { super(w, h); type = _type; flags = _flags; format = _format; } - + public LayoutParams(int w, int h, int xpos, int ypos, int _type, int _flags, int _format) { super(w, h); @@ -1608,14 +1623,14 @@ public interface WindowManager extends ViewManager { flags = _flags; format = _format; } - + public final void setTitle(CharSequence title) { if (null == title) title = ""; - + mTitle = TextUtils.stringOrSpannedString(title); } - + public final CharSequence getTitle() { return mTitle; } @@ -1660,6 +1675,7 @@ public interface WindowManager extends ViewManager { TextUtils.writeToParcel(mTitle, out, parcelableFlags); out.writeInt(screenOrientation); out.writeFloat(preferredRefreshRate); + out.writeInt(preferredDisplayModeId); out.writeInt(systemUiVisibility); out.writeInt(subtreeSystemUiVisibility); out.writeInt(hasSystemUiListeners ? 1 : 0); @@ -1683,8 +1699,8 @@ public interface WindowManager extends ViewManager { return new LayoutParams[size]; } }; - - + + public LayoutParams(Parcel in) { width = in.readInt(); height = in.readInt(); @@ -1709,6 +1725,7 @@ public interface WindowManager extends ViewManager { mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); screenOrientation = in.readInt(); preferredRefreshRate = in.readFloat(); + preferredDisplayModeId = in.readInt(); systemUiVisibility = in.readInt(); subtreeSystemUiVisibility = in.readInt(); hasSystemUiListeners = in.readInt() != 0; @@ -1757,6 +1774,8 @@ public interface WindowManager extends ViewManager { /** {@hide} */ public static final int NEEDS_MENU_KEY_CHANGED = 1 << 22; /** {@hide} */ + public static final int PREFERRED_DISPLAY_MODE_ID = 1 << 23; + /** {@hide} */ public static final int EVERYTHING_CHANGED = 0xffffffff; // internal buffer to backup/restore parameters under compatibility mode. @@ -1863,7 +1882,7 @@ public interface WindowManager extends ViewManager { rotationAnimation = o.rotationAnimation; changes |= ROTATION_ANIMATION_CHANGED; } - + if (screenOrientation != o.screenOrientation) { screenOrientation = o.screenOrientation; changes |= SCREEN_ORIENTATION_CHANGED; @@ -1874,6 +1893,11 @@ public interface WindowManager extends ViewManager { changes |= PREFERRED_REFRESH_RATE_CHANGED; } + if (preferredDisplayModeId != o.preferredDisplayModeId) { + preferredDisplayModeId = o.preferredDisplayModeId; + changes |= PREFERRED_DISPLAY_MODE_ID; + } + if (systemUiVisibility != o.systemUiVisibility || subtreeSystemUiVisibility != o.subtreeSystemUiVisibility) { systemUiVisibility = o.systemUiVisibility; @@ -1924,7 +1948,7 @@ public interface WindowManager extends ViewManager { Log.d("Debug", "WindowManager.LayoutParams={title=" + mTitle + "}"); return ""; } - + @Override public String toString() { StringBuilder sb = new StringBuilder(256); @@ -1996,6 +2020,10 @@ public interface WindowManager extends ViewManager { sb.append(" preferredRefreshRate="); sb.append(preferredRefreshRate); } + if (preferredDisplayModeId != 0) { + sb.append(" preferredDisplayMode="); + sb.append(preferredDisplayModeId); + } if (systemUiVisibility != 0) { sb.append(" sysui=0x"); sb.append(Integer.toHexString(systemUiVisibility)); diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index e7a7ba8..c16578e 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -69,12 +69,6 @@ public final class WindowManagerGlobal { public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4; /** - * The window manager is currently animating. It will call - * IWindow.doneAnimating() when done. - */ - public static final int RELAYOUT_RES_ANIMATING = 0x8; - - /** * Flag for relayout: the client will be later giving * internal insets; as a result, the window will not impact other window * layouts until the insets are given. diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index c785149..901a32d 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -1011,6 +1011,10 @@ public class AccessibilityNodeInfo implements Parcelable { public void addAction(AccessibilityAction action) { enforceNotSealed(); + addActionUnchecked(action); + } + + private void addActionUnchecked(AccessibilityAction action) { if (action == null) { return; } @@ -1491,6 +1495,15 @@ public class AccessibilityNodeInfo implements Parcelable { } /** + * Returns the actual rect containing the node bounds in screen coordinates. + * + * @hide Not safe to expose outside the framework. + */ + public Rect getBoundsInScreen() { + return mBoundsInScreen; + } + + /** * Sets the node bounds in screen coordinates. * <p> * <strong>Note:</strong> Cannot be called from an @@ -2846,9 +2859,9 @@ public class AccessibilityNodeInfo implements Parcelable { addLegacyStandardActions(legacyStandardActions); final int nonLegacyActionCount = actionCount - Integer.bitCount(legacyStandardActions); for (int i = 0; i < nonLegacyActionCount; i++) { - AccessibilityAction action = new AccessibilityAction( + final AccessibilityAction action = new AccessibilityAction( parcel.readInt(), parcel.readCharSequence()); - addAction(action); + addActionUnchecked(action); } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 040fd37..568e160 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -350,16 +350,6 @@ public final class InputMethodManager { */ private CursorAnchorInfo mCursorAnchorInfo = null; - /** - * The buffer to retrieve the view location in screen coordinates in {@link #updateCursor}. - */ - private final int[] mViewTopLeft = new int[2]; - - /** - * The matrix to convert the view location into screen coordinates in {@link #updateCursor}. - */ - private final Matrix mViewToScreenMatrix = new Matrix(); - // ----------------------------------------------------------- /** diff --git a/core/java/android/webkit/ViewAssistStructure.java b/core/java/android/webkit/ViewAssistStructure.java new file mode 100644 index 0000000..6f7a645 --- /dev/null +++ b/core/java/android/webkit/ViewAssistStructure.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.webkit; + +import android.graphics.Rect; +import android.os.Bundle; +import android.text.TextPaint; +import android.view.ViewStructure; + + +/** + * TODO This class is temporary. It will be deleted once we update Webview APK to use the + * new ViewStructure method. + * @hide + */ +public class ViewAssistStructure extends android.view.ViewAssistStructure { + + private ViewStructure mV; + + public ViewAssistStructure(ViewStructure v) { + mV = v; + } + + @Override + public void setId(int id, String packageName, String typeName, String entryName) { + mV.setId(id, packageName, typeName, entryName); + } + + @Override + public void setDimens(int left, int top, int scrollX, int scrollY, int width, + int height) { + mV.setDimens(left, top, scrollX, scrollY, width, height); + } + + @Override + public void setVisibility(int visibility) { + mV.setVisibility(visibility); + } + + @Override + public void setAssistBlocked(boolean state) { + mV.setAssistBlocked(state); + } + + @Override + public void setEnabled(boolean state) { + mV.setEnabled(state); + } + + @Override + public void setClickable(boolean state) { + mV.setClickable(state); + } + + @Override + public void setLongClickable(boolean state) { + mV.setLongClickable(state); + } + + @Override + public void setStylusButtonPressable(boolean state) { + mV.setStylusButtonPressable(state); + } + + @Override + public void setFocusable(boolean state) { + mV.setFocusable(state); + } + + @Override + public void setFocused(boolean state) { + mV.setFocused(state); + } + + @Override + public void setAccessibilityFocused(boolean state) { + mV.setAccessibilityFocused(state); + } + + @Override + public void setCheckable(boolean state) { + mV.setCheckable(state); + } + + @Override + public void setChecked(boolean state) { + mV.setChecked(state); + } + + @Override + public void setSelected(boolean state) { + mV.setSelected(state); + } + + @Override + public void setActivated(boolean state) { + mV.setActivated(state); + } + + @Override + public void setClassName(String className) { + mV.setClassName(className); + } + + @Override + public void setContentDescription(CharSequence contentDescription) { + mV.setContentDescription(contentDescription); + } + + @Override + public void setText(CharSequence text) { + mV.setText(text); + } + + @Override + public void setText(CharSequence text, int selectionStart, int selectionEnd) { + mV.setText(text, selectionStart, selectionEnd); + } + + @Override + public void setTextPaint(TextPaint paint) { + mV.setTextPaint(paint); + } + + @Override + public void setHint(CharSequence hint) { + mV.setHint(hint); + } + + @Override + public CharSequence getText() { + return mV.getText(); + } + + @Override + public int getTextSelectionStart() { + return mV.getTextSelectionStart(); + } + + @Override + public int getTextSelectionEnd() { + return mV.getTextSelectionEnd(); + } + + @Override + public CharSequence getHint() { + return mV.getHint(); + } + + @Override + public Bundle getExtras() { + return mV.getExtras(); + } + + @Override + public boolean hasExtras() { + return mV.hasExtras(); + } + + @Override + public void setChildCount(int num) { + mV.setChildCount(num); + } + + @Override + public int getChildCount() { + return mV.getChildCount(); + } + + @Override + public android.view.ViewAssistStructure newChild(int index) { + return mV.newChild(index); + } + + @Override + public android.view.ViewAssistStructure asyncNewChild(int index) { + return mV.asyncNewChild(index); + } + + @Override + public void asyncCommit() { + mV.asyncCommit(); + } + + @Override + public Rect getTempRect() { + return mV.getTempRect(); + } +} diff --git a/core/java/android/webkit/WebMessagePort.java b/core/java/android/webkit/WebMessagePort.java index eab27bd..5f33c7b 100644 --- a/core/java/android/webkit/WebMessagePort.java +++ b/core/java/android/webkit/WebMessagePort.java @@ -16,12 +16,13 @@ package android.webkit; +import android.annotation.SystemApi; import android.os.Handler; /** - * The Java representation of the HTML5 Message Port. See - * https://html.spec.whatwg.org/multipage/comms.html#messageport - * for definition of MessagePort in HTML5. + * The Java representation of the + * <a href="https://html.spec.whatwg.org/multipage/comms.html#messageport"> + * HTML5 message ports.</a> * * A Message port represents one endpoint of a Message Channel. In Android * webview, there is no separate Message Channel object. When a message channel @@ -32,6 +33,19 @@ import android.os.Handler; * When a message port is first created or received via transfer, it does not * have a WebMessageCallback to receive web messages. The messages are queued until * a WebMessageCallback is set. + * + * A message port should be closed when it is not used by the embedder application + * anymore. A closed port cannot be transferred or cannot be reopened to send + * messages. Close can be called multiple times. + * + * When a port is transferred to JS, it cannot be used to send or receive messages + * at the Java side anymore. Different from HTML5 Spec, a port cannot be transferred + * if one of these has ever happened: i. a message callback was set, ii. a message was + * posted on it. A transferred port cannot be closed by the application, since + * the ownership is also transferred. + * + * It is possible to transfer both ports of a channel to JS, for example for + * communication between subframes. */ public abstract class WebMessagePort { @@ -55,6 +69,13 @@ public abstract class WebMessagePort { } /** + * Constructor. + * @hide + */ + @SystemApi + public WebMessagePort() { } + + /** * Post a WebMessage to the entangled port. * * @param message the message from Java to JS. diff --git a/core/java/android/webkit/WebResourceError.java b/core/java/android/webkit/WebResourceError.java index 080d174..90693f3 100644 --- a/core/java/android/webkit/WebResourceError.java +++ b/core/java/android/webkit/WebResourceError.java @@ -16,6 +16,8 @@ package android.webkit; +import android.annotation.SystemApi; + /** * Encapsulates information about errors occured during loading of web resources. See * {@link WebViewClient#onReceivedError(WebView, WebResourceRequest, WebResourceError) WebViewClient.onReceivedError(WebView, WebResourceRequest, WebResourceError)} @@ -34,6 +36,16 @@ public abstract class WebResourceError { * and thus can be used for communicating the problem to the user. * * @return The description of the error + * + * Will become abstract after updated WebView.apk will be submitted + * into the Android tree. + */ + public CharSequence getDescription() { return ""; } + + /** + * This class can not be subclassed by applications. + * @hide */ - public abstract String getDescription(); + @SystemApi + public WebResourceError() {} } diff --git a/core/java/android/webkit/WebResourceResponse.java b/core/java/android/webkit/WebResourceResponse.java index a42aaa7..3a925c8 100644 --- a/core/java/android/webkit/WebResourceResponse.java +++ b/core/java/android/webkit/WebResourceResponse.java @@ -20,12 +20,15 @@ import java.io.InputStream; import java.io.StringBufferInputStream; import java.util.Map; +import android.annotation.SystemApi; + /** * Encapsulates a resource response. Applications can return an instance of this * class from {@link WebViewClient#shouldInterceptRequest} to provide a custom * response when the WebView requests a particular resource. */ public class WebResourceResponse extends WebResourceResponseBase { + private boolean mImmutable; private String mMimeType; private String mEncoding; private int mStatusCode; @@ -80,13 +83,15 @@ public class WebResourceResponse extends WebResourceResponseBase { * @param mimeType The resource response's MIME type */ public void setMimeType(String mimeType) { + checkImmutable(); mMimeType = mimeType; } /** - * {@inheritDoc} + * Gets the resource response's MIME type. + * + * @return The resource response's MIME type */ - @Override public String getMimeType() { return mMimeType; } @@ -98,13 +103,15 @@ public class WebResourceResponse extends WebResourceResponseBase { * @param encoding The resource response's encoding */ public void setEncoding(String encoding) { + checkImmutable(); mEncoding = encoding; } /** - * {@inheritDoc} + * Gets the resource response's encoding. + * + * @return The resource response's encoding */ - @Override public String getEncoding() { return mEncoding; } @@ -118,6 +125,7 @@ public class WebResourceResponse extends WebResourceResponseBase { * and not empty. */ public void setStatusCodeAndReasonPhrase(int statusCode, String reasonPhrase) { + checkImmutable(); if (statusCode < 100) throw new IllegalArgumentException("statusCode can't be less than 100."); if (statusCode > 599) @@ -140,17 +148,19 @@ public class WebResourceResponse extends WebResourceResponseBase { } /** - * {@inheritDoc} + * Gets the resource response's status code. + * + * @return The resource response's status code. */ - @Override public int getStatusCode() { return mStatusCode; } /** - * {@inheritDoc} + * Gets the description of the resource response's status code. + * + * @return The description of the resource response's status code. */ - @Override public String getReasonPhrase() { return mReasonPhrase; } @@ -161,13 +171,15 @@ public class WebResourceResponse extends WebResourceResponseBase { * @param headers Mapping of header name -> header value. */ public void setResponseHeaders(Map<String, String> headers) { + checkImmutable(); mResponseHeaders = headers; } /** - * {@inheritDoc} + * Gets the headers for the resource response. + * + * @return The headers for the resource response. */ - @Override public Map<String, String> getResponseHeaders() { return mResponseHeaders; } @@ -180,6 +192,7 @@ public class WebResourceResponse extends WebResourceResponseBase { * StringBufferInputStream. */ public void setData(InputStream data) { + checkImmutable(); // If data is (or is a subclass of) StringBufferInputStream if (data != null && StringBufferInputStream.class.isAssignableFrom(data.getClass())) { throw new IllegalArgumentException("StringBufferInputStream is deprecated and must " + @@ -189,10 +202,32 @@ public class WebResourceResponse extends WebResourceResponseBase { } /** - * {@inheritDoc} + * Gets the input stream that provides the resource response's data. + * + * @return The input stream that provides the resource response's data */ - @Override public InputStream getData() { return mInputStream; } + + /** + * The internal version of the constructor that doesn't perform arguments checks. + * @hide + */ + @SystemApi + public WebResourceResponse(boolean immutable, String mimeType, String encoding, int statusCode, + String reasonPhrase, Map<String, String> responseHeaders, InputStream data) { + mImmutable = immutable; + mMimeType = mimeType; + mEncoding = encoding; + mStatusCode = statusCode; + mReasonPhrase = reasonPhrase; + mResponseHeaders = responseHeaders; + mInputStream = data; + } + + private void checkImmutable() { + if (mImmutable) + throw new IllegalStateException("This WebResourceResponse instance is immutable"); + } } diff --git a/core/java/android/webkit/WebResourceResponseBase.java b/core/java/android/webkit/WebResourceResponseBase.java index cffde82..69eb397 100644 --- a/core/java/android/webkit/WebResourceResponseBase.java +++ b/core/java/android/webkit/WebResourceResponseBase.java @@ -16,53 +16,9 @@ package android.webkit; -import java.io.InputStream; -import java.util.Map; - /** - * Encapsulates a resource response received from the server. - * This is an abstract class used by WebView callbacks. + * This class will be deleted after updated WebView.apk will be submitted + * into the Android tree. */ public abstract class WebResourceResponseBase { - /** - * Gets the resource response's MIME type. - * - * @return The resource response's MIME type - */ - public abstract String getMimeType(); - - /** - * Gets the resource response's encoding. - * - * @return The resource response's encoding - */ - public abstract String getEncoding(); - - /** - * Gets the resource response's status code. - * - * @return The resource response's status code. - */ - public abstract int getStatusCode(); - - /** - * Gets the description of the resource response's status code. - * - * @return The description of the resource response's status code. - */ - public abstract String getReasonPhrase(); - - /** - * Gets the headers for the resource response. - * - * @return The headers for the resource response. - */ - public abstract Map<String, String> getResponseHeaders(); - - /** - * Gets the input stream that provides the resource response's data. - * - * @return The input stream that provides the resource response's data - */ - public abstract InputStream getData(); } diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 453e4f5..cfa347f 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -1340,11 +1340,13 @@ public abstract class WebSettings { * offscreen but attached to a window. Turning this on can avoid * rendering artifacts when animating an offscreen WebView on-screen. * Offscreen WebViews in this mode use more memory. The default value is - * false. + * false.<br> * Please follow these guidelines to limit memory usage: - * - WebView size should be not be larger than the device screen size. - * - Limit use of this mode to a small number of WebViews. Use it for + * <ul> + * <li> WebView size should be not be larger than the device screen size. + * <li> Limit use of this mode to a small number of WebViews. Use it for * visible WebViews and WebViews about to be animated to visible. + * </ul> */ public abstract void setOffscreenPreRaster(boolean enabled); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index e27e253..ccb98b4 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -366,15 +366,15 @@ public class WebView extends AbsoluteLayout } /** - * Callback interface supplied to {@link #insertVisualStateCallback} for receiving + * Callback interface supplied to {@link #postVisualStateCallback} for receiving * notifications about the visual state. */ public static abstract class VisualStateCallback { /** * Invoked when the visual state is ready to be drawn in the next {@link #onDraw}. * - * @param requestId the id supplied to the corresponding {@link #insertVisualStateCallback} - * request + * @param requestId The identifier passed to {@link #postVisualStateCallback} when this + * callback was posted. */ public abstract void onComplete(long requestId); } @@ -1125,15 +1125,18 @@ public class WebView extends AbsoluteLayout } /** - * Inserts a {@link VisualStateCallback}. + * Posts a {@link VisualStateCallback}, which will be called when + * the current state of the WebView is ready to be drawn. * - * <p>Updates to the the DOM are reflected asynchronously such that when the DOM is updated the - * subsequent {@link WebView#onDraw} invocation might not reflect those updates. The + * <p>Because updates to the the DOM are processed asynchronously, updates to the DOM may not + * immediately be reflected visually by subsequent {@link WebView#onDraw} invocations. The * {@link VisualStateCallback} provides a mechanism to notify the caller when the contents of - * the DOM at the current time are ready to be drawn the next time the {@link WebView} draws. - * By current time we mean the time at which this API was called. The next draw after the - * callback completes is guaranteed to reflect all the updates to the DOM applied before the - * current time, but it may also contain updates applied after the current time.</p> + * the DOM at the current time are ready to be drawn the next time the {@link WebView} + * draws.</p> + * + * <p>The next draw after the callback completes is guaranteed to reflect all the updates to the + * DOM up to the the point at which the {@link VisualStateCallback} was posted, but it may also + * contain updates applied after the callback was posted.</p> * * <p>The state of the DOM covered by this API includes the following: * <ul> @@ -1164,15 +1167,15 @@ public class WebView extends AbsoluteLayout * {@link VisualStateCallback#onComplete} method.</li> * </ul></p> * - * <p>When using this API it is also recommended to enable pre-rasterization if the - * {@link WebView} is offscreen to avoid flickering. See WebSettings#setOffscreenPreRaster for + * <p>When using this API it is also recommended to enable pre-rasterization if the {@link + * WebView} is offscreen to avoid flickering. See {@link WebSettings#setOffscreenPreRaster} for * more details and do consider its caveats.</p> * - * @param requestId an id that will be returned in the callback to allow callers to match - * requests with callbacks. - * @param callback the callback to be invoked. + * @param requestId An id that will be returned in the callback to allow callers to match + * requests with callbacks. + * @param callback The callback to be invoked. */ - public void insertVisualStateCallback(long requestId, VisualStateCallback callback) { + public void postVisualStateCallback(long requestId, VisualStateCallback callback) { checkThread(); mProvider.insertVisualStateCallback(requestId, callback); } @@ -1835,8 +1838,9 @@ public class WebView extends AbsoluteLayout /** * Creates a message channel to communicate with JS and returns the message * ports that represent the endpoints of this message channel. The HTML5 message - * channel functionality is described here: - * https://html.spec.whatwg.org/multipage/comms.html#messagechannel + * channel functionality is described + * <a href="https://html.spec.whatwg.org/multipage/comms.html#messagechannel">here + * </a> * * The returned message channels are entangled and already in started state. * @@ -1850,13 +1854,16 @@ public class WebView extends AbsoluteLayout /** * Post a message to main frame. The embedded application can restrict the * messages to a certain target origin. See - * https://html.spec.whatwg.org/multipage/comms.html#posting-messages - * for how target origin can be used. + * <a href="https://html.spec.whatwg.org/multipage/comms.html#posting-messages"> + * HTML5 spec</a> for how target origin can be used. * * @param message the WebMessage - * @param targetOrigin the target origin. + * @param targetOrigin the target origin. This is the origin of the page + * that is intended to receive the message. For best security + * practices, the user should not specify a wildcard (*) when + * specifying the origin. */ - public void postMessageToMainFrame(WebMessage message, Uri targetOrigin) { + public void postWebMessage(WebMessage message, Uri targetOrigin) { checkThread(); mProvider.postMessageToMainFrame(message, targetOrigin); } @@ -2429,7 +2436,8 @@ public class WebView extends AbsoluteLayout @Override public void onProvideVirtualStructure(ViewStructure structure) { - mProvider.getViewDelegate().onProvideVirtualAssistStructure(structure); + ViewAssistStructure s = new ViewAssistStructure(structure); + mProvider.getViewDelegate().onProvideVirtualAssistStructure(s); } /** @hide */ diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index 8a2b3fa..feed2b8 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -83,27 +83,31 @@ public class WebViewClient { } /** - * Notify the host application that the page commit is visible. + * Notify the host application that {@link android.webkit.WebView} content left over from + * previous page navigations will no longer be drawn. * - * <p>This is the earliest point at which we can guarantee that the contents of the previously - * loaded page will not longer be drawn in the next {@link WebView#onDraw}. The next draw will - * render the {@link WebView#setBackgroundColor background color} of the WebView or some of the - * contents from the committed page already. This callback may be useful when reusing - * {@link WebView}s to ensure that no stale content is shown. This method is only called for - * the main frame.</p> + * <p>This callback can be used to determine the point at which it is safe to make a recycled + * {@link android.webkit.WebView} visible, ensuring that no stale content is shown. It is called + * at the earliest point at which it can be guaranteed that {@link WebView#onDraw} will no + * longer draw any content from previous navigations. The next draw will display either the + * {@link WebView#setBackgroundColor background color} of the {@link WebView}, or some of the + * contents of the newly loaded page. * - * <p>This method is called when the state of the DOM at the point at which the - * body of the HTTP response (commonly the string of html) had started loading will be visible. - * If you set a background color for the page in the HTTP response body this will most likely - * be visible and perhaps some other elements. At that point no other resources had usually - * been loaded, so you can expect images for example to not be visible. If you want - * a finer level of granularity consider calling {@link WebView#insertVisualStateCallback} - * directly.</p> + * <p>This method is called when the body of the HTTP response has started loading, is reflected + * in the DOM, and will be visible in subsequent draws. This callback occurs early in the + * document loading process, and as such you should expect that linked resources (for example, + * css and images) may not be available.</p> * - * <p>Please note that all the conditions and recommendations presented in - * {@link WebView#insertVisualStateCallback} also apply to this API.<p> + * <p>For more fine-grained notification of visual state updates, see {@link + * WebView#postVisualStateCallback}.</p> * - * @param url the url of the committed page + * <p>Please note that all the conditions and recommendations applicable to + * {@link WebView#postVisualStateCallback} also apply to this API.<p> + * + * <p>This callback is only called for main frame navigations.</p> + * + * @param view The {@link android.webkit.WebView} for which the navigation occurred. + * @param url The URL corresponding to the page navigation that triggered this callback. */ public void onPageCommitVisible(WebView view, String url) { } @@ -229,11 +233,20 @@ public class WebViewClient { public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { if (request.isForMainFrame()) { onReceivedError(view, - error.getErrorCode(), error.getDescription(), request.getUrl().toString()); + error.getErrorCode(), error.getDescription().toString(), + request.getUrl().toString()); } } /** + * This method will be deleted after updated WebView.apk will be submitted + * into the Android tree. + */ + public void onReceivedHttpError( + WebView view, WebResourceRequest request, WebResourceResponseBase errorResponse) { + } + + /** * Notify the host application that an HTTP error has been received from the server while * loading a resource. HTTP errors have status codes >= 400. This callback will be called * for any resource (iframe, image, etc), not just for the main page. Thus, it is recommended to @@ -244,7 +257,7 @@ public class WebViewClient { * @param errorResponse Information about the error occured. */ public void onReceivedHttpError( - WebView view, WebResourceRequest request, WebResourceResponseBase errorResponse) { + WebView view, WebResourceRequest request, WebResourceResponse errorResponse) { } /** diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 9782d72..b7d529e 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -40,7 +40,10 @@ import com.android.server.LocalServices; import dalvik.system.VMRuntime; import java.io.File; +import java.io.IOException; import java.util.Arrays; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; /** * Top level factory, used creating all the main WebView implementation classes. @@ -323,15 +326,30 @@ public final class WebViewFactory { long newVmSize = 0L; for (String path : nativeLibs) { + if (path == null || TextUtils.isEmpty(path)) continue; if (DEBUG) Log.d(LOGTAG, "Checking file size of " + path); - if (path == null) continue; File f = new File(path); if (f.exists()) { - long length = f.length(); - if (length > newVmSize) { - newVmSize = length; + newVmSize = Math.max(newVmSize, f.length()); + continue; + } + if (path.contains("!")) { + String[] split = TextUtils.split(path, "!"); + if (split.length == 2) { + try { + ZipFile z = new ZipFile(split[0]); + ZipEntry e = z.getEntry(split[1]); + if (e != null && e.getMethod() == ZipEntry.STORED) { + newVmSize = Math.max(newVmSize, e.getSize()); + continue; + } + } + catch (IOException e) { + Log.e(LOGTAG, "error reading APK file " + split[0] + ", ", e); + } } } + Log.e(LOGTAG, "error sizing load for " + path); } if (DEBUG) { @@ -355,6 +373,27 @@ public final class WebViewFactory { } // throws MissingWebViewPackageException + private static String getLoadFromApkPath(String apkPath, + String[] abiList, + String nativeLibFileName) { + // Search the APK for a native library conforming to a listed ABI. + try { + ZipFile z = new ZipFile(apkPath); + for (String abi : abiList) { + final String entry = "lib/" + abi + "/" + nativeLibFileName; + ZipEntry e = z.getEntry(entry); + if (e != null && e.getMethod() == ZipEntry.STORED) { + // Return a path formatted for dlopen() load from APK. + return apkPath + "!" + entry; + } + } + } catch (IOException e) { + throw new MissingWebViewPackageException(e); + } + return ""; + } + + // throws MissingWebViewPackageException private static String[] getWebViewNativeLibraryPaths() { ApplicationInfo ai = getWebViewApplicationInfo(); final String NATIVE_LIB_FILE_NAME = getWebViewLibrary(ai); @@ -382,8 +421,29 @@ public final class WebViewFactory { path32 = ai.nativeLibraryDir; path64 = ""; } - if (!TextUtils.isEmpty(path32)) path32 += "/" + NATIVE_LIB_FILE_NAME; - if (!TextUtils.isEmpty(path64)) path64 += "/" + NATIVE_LIB_FILE_NAME; + + // Form the full paths to the extracted native libraries. + // If libraries were not extracted, try load from APK paths instead. + if (!TextUtils.isEmpty(path32)) { + path32 += "/" + NATIVE_LIB_FILE_NAME; + File f = new File(path32); + if (!f.exists()) { + path32 = getLoadFromApkPath(ai.sourceDir, + Build.SUPPORTED_32_BIT_ABIS, + NATIVE_LIB_FILE_NAME); + } + } + if (!TextUtils.isEmpty(path64)) { + path64 += "/" + NATIVE_LIB_FILE_NAME; + File f = new File(path64); + if (!f.exists()) { + path64 = getLoadFromApkPath(ai.sourceDir, + Build.SUPPORTED_64_BIT_ABIS, + NATIVE_LIB_FILE_NAME); + } + } + + if (DEBUG) Log.v(LOGTAG, "Native 32-bit lib: " + path32 + ", 64-bit lib: " + path64); return new String[] { path32, path64 }; } diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java index e367192..00aba2a 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -32,7 +32,6 @@ import android.print.PrintDocumentAdapter; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.ViewStructure; import android.view.ViewGroup.LayoutParams; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -299,7 +298,7 @@ public interface WebViewProvider { interface ViewDelegate { public boolean shouldDelayChildPressedState(); - public void onProvideVirtualAssistStructure(ViewStructure structure); + public void onProvideVirtualAssistStructure(android.view.ViewAssistStructure structure); public AccessibilityNodeProvider getAccessibilityNodeProvider(); diff --git a/core/java/android/widget/ActivityChooserModel.java b/core/java/android/widget/ActivityChooserModel.java index 51174c3..75c857c 100644 --- a/core/java/android/widget/ActivityChooserModel.java +++ b/core/java/android/widget/ActivityChooserModel.java @@ -40,6 +40,7 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -991,7 +992,7 @@ public class ActivityChooserModel extends DataSetObservable { } try { XmlPullParser parser = Xml.newPullParser(); - parser.setInput(fis, null); + parser.setInput(fis, StandardCharsets.UTF_8.name()); int type = XmlPullParser.START_DOCUMENT; while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { @@ -1074,7 +1075,7 @@ public class ActivityChooserModel extends DataSetObservable { try { serializer.setOutput(fos, null); - serializer.startDocument("UTF-8", true); + serializer.startDocument(StandardCharsets.UTF_8.name(), true); serializer.startTag(null, TAG_HISTORICAL_RECORDS); final int recordCount = historicalRecords.size(); diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java index 6feb94b..bb4a948 100644 --- a/core/java/android/widget/AppSecurityPermissions.java +++ b/core/java/android/widget/AppSecurityPermissions.java @@ -244,7 +244,7 @@ public class AppSecurityPermissions { @Override public void onClick(DialogInterface dialog, int which) { PackageManager pm = getContext().getPackageManager(); - pm.revokePermission(mPackageName, mPerm.name, + pm.revokeRuntimePermission(mPackageName, mPerm.name, new UserHandle(mContext.getUserId())); PermissionItemView.this.setVisibility(View.GONE); } diff --git a/core/java/android/widget/DayPickerPagerAdapter.java b/core/java/android/widget/DayPickerPagerAdapter.java index d271af2..8fe8252 100644 --- a/core/java/android/widget/DayPickerPagerAdapter.java +++ b/core/java/android/widget/DayPickerPagerAdapter.java @@ -186,11 +186,12 @@ class DayPickerPagerAdapter extends PagerAdapter { } private int getMonthForPosition(int position) { - return position % MONTHS_IN_YEAR + mMinDate.get(Calendar.MONTH); + return (position + mMinDate.get(Calendar.MONTH)) % MONTHS_IN_YEAR; } private int getYearForPosition(int position) { - return position / MONTHS_IN_YEAR + mMinDate.get(Calendar.YEAR); + final int yearOffset = (position + mMinDate.get(Calendar.MONTH)) / MONTHS_IN_YEAR; + return yearOffset + mMinDate.get(Calendar.YEAR); } private int getPositionForDay(@Nullable Calendar day) { @@ -198,8 +199,8 @@ class DayPickerPagerAdapter extends PagerAdapter { return -1; } - final int yearOffset = (day.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR)); - final int monthOffset = (day.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH)); + final int yearOffset = day.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR); + final int monthOffset = day.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH); final int position = yearOffset * MONTHS_IN_YEAR + monthOffset; return position; } diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java index 334afab..dc772fb 100644 --- a/core/java/android/widget/DayPickerView.java +++ b/core/java/android/widget/DayPickerView.java @@ -344,6 +344,8 @@ class DayPickerView extends ViewGroup { // Changing the min/max date changes the selection position since we // don't really have stable IDs. Jumps immediately to the new position. setDate(mSelectedDay.getTimeInMillis(), false, false); + + updateButtonVisibility(mViewPager.getCurrentItem()); } /** diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 4fd85b6..c829783 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -209,6 +209,10 @@ public class Editor { // Set when this TextView gained focus with some text selected. Will start selection mode. boolean mCreatedWithASelection; + boolean mDoubleTap = false; + + private Runnable mSelectionModeWithoutSelectionRunnable; + // The span controller helps monitoring the changes to which the Editor needs to react: // - EasyEditSpans, for which we have some UI to display on attach and on hide // - SelectionSpans, for which we need to call updateSelection if an IME is attached @@ -286,6 +290,13 @@ public class Editor { mUndoManager.redo(owners, 1); // Redo 1 action. } + void replace() { + int middle = (mTextView.getSelectionStart() + mTextView.getSelectionEnd()) / 2; + stopSelectionActionMode(); + Selection.setSelection((Spannable) mTextView.getText(), middle); + showSuggestions(); + } + void onAttachedToWindow() { if (mShowErrorAfterAttach) { showError(); @@ -342,6 +353,11 @@ public class Editor { mTextView.removeCallbacks(mShowSuggestionRunnable); } + // Cancel the single tap delayed runnable. + if (mSelectionModeWithoutSelectionRunnable != null) { + mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable); + } + destroyDisplayListsData(); if (mSpellChecker != null) { @@ -689,14 +705,12 @@ public class Editor { // FIXME - For this and similar methods we're not doing anything to check if there's // a LocaleSpan in the text, this may be something we should try handling or checking for. int retOffset = getWordIteratorWithText().prevBoundary(offset); - if (isPunctBoundaryBehind(retOffset, true /* isStart */)) { - // If we're on a punctuation boundary we should continue to get the - // previous offset until we're not longer on a punctuation boundary. - retOffset = getWordIteratorWithText().prevBoundary(retOffset); - while (!isPunctBoundaryBehind(retOffset, false /* isStart */) - && retOffset != BreakIterator.DONE) { - retOffset = getWordIteratorWithText().prevBoundary(retOffset); - } + if (getWordIteratorWithText().isOnPunctuation(retOffset)) { + // On punctuation boundary or within group of punctuation, find punctuation start. + retOffset = getWordIteratorWithText().getPunctuationBeginning(offset); + } else { + // Not on a punctuation boundary, find the word start. + retOffset = getWordIteratorWithText().getBeginning(offset); } if (retOffset == BreakIterator.DONE) { return offset; @@ -706,14 +720,12 @@ public class Editor { private int getWordEnd(int offset) { int retOffset = getWordIteratorWithText().nextBoundary(offset); - if (isPunctBoundaryForward(retOffset, true /* isStart */)) { - // If we're on a punctuation boundary we should continue to get the - // next offset until we're no longer on a punctuation boundary. - retOffset = getWordIteratorWithText().nextBoundary(retOffset); - while (!isPunctBoundaryForward(retOffset, false /* isStart */) - && retOffset != BreakIterator.DONE) { - retOffset = getWordIteratorWithText().nextBoundary(retOffset); - } + if (getWordIteratorWithText().isAfterPunctuation(retOffset)) { + // On punctuation boundary or within group of punctuation, find punctuation end. + retOffset = getWordIteratorWithText().getPunctuationEnd(offset); + } else { + // Not on a punctuation boundary, find the word end. + retOffset = getWordIteratorWithText().getEnd(offset); } if (retOffset == BreakIterator.DONE) { return offset; @@ -722,70 +734,6 @@ public class Editor { } /** - * Checks for punctuation boundaries for the provided offset and the - * previous character. - * - * @param offset The offset to check from. - * @param isStart Whether the boundary being checked for is at the start or - * end of a punctuation sequence. - * @return Whether this is a punctuation boundary. - */ - private boolean isPunctBoundaryBehind(int offset, boolean isStart) { - CharSequence text = mTextView.getText(); - if (offset == BreakIterator.DONE || offset > text.length() || offset == 0) { - return false; - } - int cp = Character.codePointAt(text, offset); - int prevCp = Character.codePointBefore(text, offset); - - if (isPunctuation(cp)) { - // If it's the start, the current cp and the prev cp are - // punctuation. If it's at the end of a punctuation sequence the - // current is punctuation and the prev is not. - return isStart ? isPunctuation(prevCp) : !isPunctuation(prevCp); - } - return false; - } - - /** - * Checks for punctuation boundaries for the provided offset and the next - * character. - * - * @param offset The offset to check from. - * @param isStart Whether the boundary being checked for is at the start or - * end of a punctuation sequence. - * @return Whether this is a punctuation boundary. - */ - private boolean isPunctBoundaryForward(int offset, boolean isStart) { - CharSequence text = mTextView.getText(); - if (offset == BreakIterator.DONE || offset > text.length() || offset == 0) { - return false; - } - int cp = Character.codePointBefore(text, offset); - int nextCpOffset = Math.min(offset + Character.charCount(cp), text.length() - 1); - int nextCp = Character.codePointBefore(text, nextCpOffset); - - if (isPunctuation(cp)) { - // If it's the start, the current cp and the next cp are - // punctuation. If it's at the end of a punctuation sequence the - // current is punctuation and the next is not. - return isStart ? isPunctuation(nextCp) : !isPunctuation(nextCp); - } - return false; - } - - private boolean isPunctuation(int cp) { - int type = Character.getType(cp); - return (type == Character.CONNECTOR_PUNCTUATION || - type == Character.DASH_PUNCTUATION || - type == Character.END_PUNCTUATION || - type == Character.FINAL_QUOTE_PUNCTUATION || - type == Character.INITIAL_QUOTE_PUNCTUATION || - type == Character.OTHER_PUNCTUATION || - type == Character.START_PUNCTUATION); - } - - /** * Adjusts selection to the word under last touch offset. Return true if the operation was * successfully performed. */ @@ -1867,6 +1815,7 @@ public class Editor { // When the cursor moves, the word that was typed may need spell check mSpellChecker.onSelectionChanged(); } + if (!extractedTextModeWillBeStarted()) { if (isCursorInsideEasyCorrectionSpan()) { mShowSuggestionRunnable = new Runnable() { @@ -3185,10 +3134,6 @@ public class Editor { mCustomSelectionActionModeCallback.onActionItemClicked(mode, item)) { return true; } - if (item.getItemId() == TextView.ID_REPLACE) { - onReplace(); - return true; - } return mTextView.onTextContextMenuItem(item.getItemId()); } @@ -3262,13 +3207,6 @@ public class Editor { } } - private void onReplace() { - int middle = (mTextView.getSelectionStart() + mTextView.getSelectionEnd()) / 2; - stopSelectionActionMode(); - Selection.setSelection((Spannable) mTextView.getText(), middle); - showSuggestions(); - } - /** * A listener to call {@link InputMethodManager#updateCursorAnchorInfo(View, CursorAnchorInfo)} * while the input method is requesting the cursor/anchor position. Does nothing as long as @@ -3488,9 +3426,13 @@ public class Editor { protected void updateDrawable() { final int offset = getCurrentCursorOffset(); final boolean isRtlCharAtOffset = mTextView.getLayout().isRtlCharAt(offset); + final Drawable oldDrawable = mDrawable; mDrawable = isRtlCharAtOffset ? mDrawableRtl : mDrawableLtr; mHotspotX = getHotspotX(mDrawable, isRtlCharAtOffset); mHorizontalGravity = getHorizontalGravity(isRtlCharAtOffset); + if (oldDrawable != mDrawable) { + postInvalidate(); + } } protected abstract int getHotspotX(Drawable drawable, boolean isRtlRun); @@ -3794,9 +3736,27 @@ public class Editor { super.show(); final long durationSinceCutOrCopy = - SystemClock.uptimeMillis() - TextView.LAST_CUT_OR_COPY_TIME; - if (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION) { - startSelectionActionModeWithoutSelection(); + SystemClock.uptimeMillis() - TextView.sLastCutCopyOrTextChangedTime; + + // Cancel the single tap delayed runnable. + if (mDoubleTap && mSelectionModeWithoutSelectionRunnable != null) { + mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable); + } + + // Prepare and schedule the single tap runnable to run exactly after the double tap + // timeout has passed. + if (!mDoubleTap && (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION)) { + if (mSelectionModeWithoutSelectionRunnable == null) { + mSelectionModeWithoutSelectionRunnable = new Runnable() { + public void run() { + startSelectionActionModeWithoutSelection(); + } + }; + } + + mTextView.postDelayed( + mSelectionModeWithoutSelectionRunnable, + ViewConfiguration.getDoubleTapTimeout() + 1); } hideAfterDelay(); @@ -3902,6 +3862,9 @@ public class Editor { @Override public void updatePosition(float x, float y) { positionAtCursorOffset(mTextView.getOffsetForPosition(x, y), false); + if (mSelectionActionMode != null) { + mSelectionActionMode.invalidate(); + } } @Override @@ -3920,8 +3883,8 @@ public class Editor { private class SelectionStartHandleView extends HandleView { // Indicates whether the cursor is making adjustments within a word. private boolean mInWord = false; - // Offset to track difference between touch and word boundary. - protected int mTouchWordOffset; + // Difference between touch position and word boundary position. + private float mTouchWordDelta; public SelectionStartHandleView(Drawable drawableLtr, Drawable drawableRtl) { super(drawableLtr, drawableRtl); @@ -3934,7 +3897,7 @@ public class Editor { @Override protected int getHorizontalGravity(boolean isRtlRun) { - return isRtlRun ? Gravity.RIGHT : Gravity.LEFT; + return isRtlRun ? Gravity.LEFT : Gravity.RIGHT; } @Override @@ -3954,10 +3917,20 @@ public class Editor { @Override public void updatePosition(float x, float y) { - final int trueOffset = mTextView.getOffsetForPosition(x, y); - final int currLine = mTextView.getLineAtCoordinate(y); + final int selectionEnd = mTextView.getSelectionEnd(); + final Layout layout = mTextView.getLayout(); + int initialOffset = mTextView.getOffsetForPosition(x, y); + int currLine = mTextView.getLineAtCoordinate(y); boolean positionCursor = false; - int offset = trueOffset; + + if (initialOffset >= selectionEnd) { + // Handles have crossed, bound it to the last selected line and + // adjust by word / char as normal. + currLine = layout != null ? layout.getLineForOffset(selectionEnd) : mPrevLine; + initialOffset = mTextView.getOffsetAtCoordinate(currLine, x); + } + + int offset = initialOffset; int end = getWordEnd(offset); int start = getWordStart(offset); @@ -3973,33 +3946,41 @@ public class Editor { offset = mPreviousOffset; } } - mTouchWordOffset = Math.max(trueOffset - offset, 0); - positionCursor = true; - } else if (offset - mTouchWordOffset > mPreviousOffset || currLine > mPrevLine) { - // User is shrinking the selection. - if (currLine > mPrevLine) { - // We're on a different line, so we'll snap to word boundaries. - offset = start; - mTouchWordOffset = Math.max(trueOffset - offset, 0); + if (layout != null && offset < initialOffset) { + final float adjustedX = layout.getPrimaryHorizontal(offset); + mTouchWordDelta = + mTextView.convertToLocalHorizontalCoordinate(x) - adjustedX; } else { - offset -= mTouchWordOffset; + mTouchWordDelta = 0.0f; } positionCursor = true; + } else { + final int adjustedOffset = + mTextView.getOffsetAtCoordinate(currLine, x - mTouchWordDelta); + if (adjustedOffset > mPreviousOffset || currLine > mPrevLine) { + // User is shrinking the selection. + if (currLine > mPrevLine) { + // We're on a different line, so we'll snap to word boundaries. + offset = start; + if (layout != null && offset < initialOffset) { + final float adjustedX = layout.getPrimaryHorizontal(offset); + mTouchWordDelta = + mTextView.convertToLocalHorizontalCoordinate(x) - adjustedX; + } else { + mTouchWordDelta = 0.0f; + } + } else { + offset = adjustedOffset; + } + positionCursor = true; + } } - // Handles can not cross and selection is at least one character. if (positionCursor) { - final int selectionEnd = mTextView.getSelectionEnd(); + // Handles can not cross and selection is at least one character. if (offset >= selectionEnd) { - // We can't cross the handles so let's just constrain the Y value. - int alteredOffset = mTextView.getOffsetAtCoordinate(mPrevLine, x); - if (alteredOffset >= selectionEnd) { - // Can't pass the other drag handle. - offset = getNextCursorOffset(selectionEnd, false); - } else { - offset = alteredOffset; - } - mTouchWordOffset = 0; + offset = getNextCursorOffset(selectionEnd, false); + mTouchWordDelta = 0.0f; } mInWord = !getWordIteratorWithText().isBoundary(offset); positionAtCursorOffset(offset, false); @@ -4011,7 +3992,7 @@ public class Editor { boolean superResult = super.onTouchEvent(event); if (event.getActionMasked() == MotionEvent.ACTION_UP) { // Reset the touch word offset when the user has lifted their finger. - mTouchWordOffset = 0; + mTouchWordDelta = 0.0f; } return superResult; } @@ -4020,8 +4001,8 @@ public class Editor { private class SelectionEndHandleView extends HandleView { // Indicates whether the cursor is making adjustments within a word. private boolean mInWord = false; - // Offset to track difference between touch and word boundary. - protected int mTouchWordOffset; + // Difference between touch position and word boundary position. + private float mTouchWordDelta; public SelectionEndHandleView(Drawable drawableLtr, Drawable drawableRtl) { super(drawableLtr, drawableRtl); @@ -4034,7 +4015,7 @@ public class Editor { @Override protected int getHorizontalGravity(boolean isRtlRun) { - return isRtlRun ? Gravity.LEFT : Gravity.RIGHT; + return isRtlRun ? Gravity.RIGHT : Gravity.LEFT; } @Override @@ -4054,10 +4035,20 @@ public class Editor { @Override public void updatePosition(float x, float y) { - final int trueOffset = mTextView.getOffsetForPosition(x, y); - final int currLine = mTextView.getLineAtCoordinate(y); - int offset = trueOffset; + final int selectionStart = mTextView.getSelectionStart(); + final Layout layout = mTextView.getLayout(); + int initialOffset = mTextView.getOffsetForPosition(x, y); + int currLine = mTextView.getLineAtCoordinate(y); boolean positionCursor = false; + + if (initialOffset <= selectionStart) { + // Handles have crossed, bound it to the first selected line and + // adjust by word / char as normal. + currLine = layout != null ? layout.getLineForOffset(selectionStart) : mPrevLine; + initialOffset = mTextView.getOffsetAtCoordinate(currLine, x); + } + + int offset = initialOffset; int end = getWordEnd(offset); int start = getWordStart(offset); @@ -4073,33 +4064,41 @@ public class Editor { offset = mPreviousOffset; } } - mTouchWordOffset = Math.max(offset - trueOffset, 0); - positionCursor = true; - } else if (offset + mTouchWordOffset < mPreviousOffset || currLine < mPrevLine) { - // User is shrinking the selection. - if (currLine < mPrevLine) { - // We're on a different line, so we'll snap to word boundaries. - offset = end; - mTouchWordOffset = Math.max(offset - trueOffset, 0); + if (layout != null && offset > initialOffset) { + final float adjustedX = layout.getPrimaryHorizontal(offset); + mTouchWordDelta = + adjustedX - mTextView.convertToLocalHorizontalCoordinate(x); } else { - offset += mTouchWordOffset; + mTouchWordDelta = 0.0f; } positionCursor = true; + } else { + final int adjustedOffset = + mTextView.getOffsetAtCoordinate(currLine, x + mTouchWordDelta); + if (adjustedOffset < mPreviousOffset || currLine < mPrevLine) { + // User is shrinking the selection. + if (currLine < mPrevLine) { + // We're on a different line, so we'll snap to word boundaries. + offset = end; + if (layout != null && offset > initialOffset) { + final float adjustedX = layout.getPrimaryHorizontal(offset); + mTouchWordDelta = + adjustedX - mTextView.convertToLocalHorizontalCoordinate(x); + } else { + mTouchWordDelta = 0.0f; + } + } else { + offset = adjustedOffset; + } + positionCursor = true; + } } if (positionCursor) { - final int selectionStart = mTextView.getSelectionStart(); + // Handles can not cross and selection is at least one character. if (offset <= selectionStart) { - // We can't cross the handles so let's just constrain the Y value. - int alteredOffset = mTextView.getOffsetAtCoordinate(mPrevLine, x); - int length = mTextView.getText().length(); - if (alteredOffset <= selectionStart) { - // Can't pass the other drag handle. - offset = getNextCursorOffset(selectionStart, true); - } else { - offset = Math.min(alteredOffset, length); - } - mTouchWordOffset = 0; + offset = getNextCursorOffset(selectionStart, true); + mTouchWordDelta = 0.0f; } mInWord = !getWordIteratorWithText().isBoundary(offset); positionAtCursorOffset(offset, false); @@ -4111,7 +4110,7 @@ public class Editor { boolean superResult = super.onTouchEvent(event); if (event.getActionMasked() == MotionEvent.ACTION_UP) { // Reset the touch word offset when the user has lifted their finger. - mTouchWordOffset = 0; + mTouchWordDelta = 0.0f; } return superResult; } @@ -4146,6 +4145,10 @@ public class Editor { public void show() { getHandle().show(); + + if (mSelectionModifierCursorController != null) { + mSelectionModifierCursorController.hide(); + } } public void hide() { @@ -4187,8 +4190,6 @@ public class Editor { // The offsets of that last touch down event. Remembered to start selection there. private int mMinTouchOffset, mMaxTouchOffset; - // Double tap detection - private long mPreviousTapUpTime = 0; private float mDownPositionX, mDownPositionY; private boolean mGestureStayedInTapRegion; @@ -4270,8 +4271,7 @@ public class Editor { // Double tap detection if (mGestureStayedInTapRegion) { - long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime; - if (duration <= ViewConfiguration.getDoubleTapTimeout()) { + if (mDoubleTap) { final float deltaX = x - mDownPositionX; final float deltaY = y - mDownPositionY; final float distanceSquared = deltaX * deltaX + deltaY * deltaY; @@ -4380,7 +4380,6 @@ public class Editor { break; case MotionEvent.ACTION_UP: - mPreviousTapUpTime = SystemClock.uptimeMillis(); if (mDragAcceleratorActive) { // No longer dragging to select text, let the parent intercept events. mTextView.getParent().requestDisallowInterceptTouchEvent(false); @@ -4472,7 +4471,7 @@ public class Editor { private class CorrectionHighlighter { private final Path mPath = new Path(); - private final Paint mPaint = new Paint(); + private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private int mStart, mEnd; private long mFadingStartTime; private RectF mTempRectF; diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 05059bc..73a873a 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -36,8 +36,10 @@ import android.graphics.RectF; import android.graphics.Xfermode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Build; +import android.os.Handler; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; @@ -462,6 +464,21 @@ public class ImageView extends View { } /** + * Sets the content of this ImageView to the specified Icon. + * + * <p class="note">Depending on the Icon type, this may do Bitmap reading and decoding + * on the UI thread, which can cause UI jank. If that's a concern, consider using + * {@link Icon#loadDrawableAsync(Context, Icon.OnDrawableLoadedListener, Handler)} + * and then {@link #setImageDrawable(android.graphics.drawable.Drawable)} instead.</p> + * + * @param icon an Icon holding the desired image + */ + @android.view.RemotableViewMethod + public void setImageIcon(Icon icon) { + setImageDrawable(icon.loadDrawable(mContext)); + } + + /** * Applies a tint to the image drawable. Does not modify the current tint * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. * <p> diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index a10be11..dc75fd0 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -35,6 +35,7 @@ import android.graphics.Bitmap; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -1072,6 +1073,7 @@ public class RemoteViews implements Parcelable, Filter { static final int BUNDLE = 13; static final int INTENT = 14; static final int COLOR_STATE_LIST = 15; + static final int ICON = 16; String methodName; int type; @@ -1150,6 +1152,10 @@ public class RemoteViews implements Parcelable, Filter { this.value = ColorStateList.CREATOR.createFromParcel(in); } break; + case ICON: + if (in.readInt() != 0) { + this.value = Icon.CREATOR.createFromParcel(in); + } default: break; } @@ -1225,6 +1231,13 @@ public class RemoteViews implements Parcelable, Filter { if (this.value != null) { ((ColorStateList)this.value).writeToParcel(out, flags); } + break; + case ICON: + out.writeInt(this.value != null ? 1 : 0); + if (this.value != null) { + ((Icon)this.value).writeToParcel(out, flags); + } + break; default: break; } @@ -1262,6 +1275,8 @@ public class RemoteViews implements Parcelable, Filter { return Intent.class; case COLOR_STATE_LIST: return ColorStateList.class; + case ICON: + return Icon.class; default: return null; } @@ -2082,6 +2097,16 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Equivalent to calling ImageView.setImageIcon + * + * @param viewId The id of the view whose bitmap should change + * @param icon The new Icon for the ImageView + */ + public void setImageViewIcon(int viewId, Icon icon) { + setIcon(viewId, "setImageIcon", icon); + } + + /** * Equivalent to calling AdapterView.setEmptyView * * @param viewId The id of the view on which to set the empty view @@ -2519,6 +2544,17 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Call a method taking one Icon on a view in the layout for this RemoteViews. + * + * @param viewId The id of the view on which to call the method. + * @param methodName The name of the method to call. + * @param value The {@link android.graphics.drawable.Icon} to pass the method. + */ + public void setIcon(int viewId, String methodName, Icon value) { + addAction(new ReflectionAction(viewId, methodName, ReflectionAction.ICON, value)); + } + + /** * Equivalent to calling View.setContentDescription(CharSequence). * * @param viewId The id of the view whose content description should change. diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index ff587c2..f42959f 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -26,6 +26,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Insets; +import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Typeface; @@ -214,7 +215,7 @@ public class Switch extends CompoundButton { public Switch(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - mTextPaint = new TextPaint(); + mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); final Resources res = getResources(); mTextPaint.density = res.getDisplayMetrics().density; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 68c49cd..5acd79f 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -238,6 +238,7 @@ import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; * @attr ref android.R.styleable#TextView_letterSpacing * @attr ref android.R.styleable#TextView_fontFeatureSettings * @attr ref android.R.styleable#TextView_breakStrategy + * @attr ref android.R.styleable#TextView_hyphenationFrequency * @attr ref android.R.styleable#TextView_leftIndents * @attr ref android.R.styleable#TextView_rightIndents */ @@ -291,8 +292,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // New state used to change background based on whether this TextView is multiline. private static final int[] MULTILINE_STATE_SET = { R.attr.state_multiline }; - // System wide time for last cut or copy action. - static long LAST_CUT_OR_COPY_TIME; + // System wide time for last cut, copy or text changed action. + static long sLastCutCopyOrTextChangedTime; /** * @hide @@ -555,6 +556,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private float mSpacingAdd = 0.0f; private int mBreakStrategy; + private int mHyphenationFrequency; private int[] mLeftIndents; private int[] mRightIndents; @@ -597,6 +599,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private final Paint mHighlightPaint; private boolean mHighlightPathBogus = true; + private boolean mFirstTouch = false; + private long mLastTouchUpTime = 0; + // Although these fields are specific to editable text, they are not added to Editor because // they are defined by the TextView's style and are theme-dependent. int mCursorDrawableRes; @@ -669,11 +674,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final Resources res = getResources(); final CompatibilityInfo compat = res.getCompatibilityInfo(); - mTextPaint = new TextPaint(); + mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); mTextPaint.density = res.getDisplayMetrics().density; mTextPaint.setCompatibilityScaling(compat.applicationScale); - mHighlightPaint = new Paint(); + mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mHighlightPaint.setCompatibilityScaling(compat.applicationScale); mMovement = getDefaultMovementMethod(); @@ -696,6 +701,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener float letterSpacing = 0; String fontFeatureSettings = null; mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; + mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE; final Resources.Theme theme = context.getTheme(); @@ -1154,6 +1160,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mBreakStrategy = a.getInt(attr, Layout.BREAK_STRATEGY_SIMPLE); break; + case com.android.internal.R.styleable.TextView_hyphenationFrequency: + mHyphenationFrequency = a.getInt(attr, Layout.HYPHENATION_FREQUENCY_NONE); + break; + case com.android.internal.R.styleable.TextView_leftIndents: TypedArray margins = res.obtainTypedArray(a.getResourceId(attr, View.NO_ID)); mLeftIndents = parseDimensionArray(margins); @@ -3050,6 +3060,33 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Sets the hyphenation frequency. The default value for both TextView and EditText, which is set + * from the theme, is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}. + * + * @attr ref android.R.styleable#TextView_hyphenationFrequency + * @see #getHyphenationFrequency() + */ + public void setHyphenationFrequency(@Layout.HyphenationFrequency int hyphenationFrequency) { + mHyphenationFrequency = hyphenationFrequency; + if (mLayout != null) { + nullLayouts(); + requestLayout(); + invalidate(); + } + } + + /** + * @return the currently set hyphenation frequency. + * + * @attr ref android.R.styleable#TextView_hyphenationFrequency + * @see #setHyphenationFrequency(int) + */ + @Layout.HyphenationFrequency + public int getHyphenationFrequency() { + return mHyphenationFrequency; + } + + /** * Set indents. Arguments are arrays holding an indent amount, one per line, measured in * pixels. For lines past the last element in the array, the last element repeats. * @@ -6637,7 +6674,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setTextDir(mTextDir) .setLineSpacing(mSpacingAdd, mSpacingMult) .setIncludePad(mIncludePad) - .setBreakStrategy(mBreakStrategy); + .setBreakStrategy(mBreakStrategy) + .setHyphenationFrequency(mHyphenationFrequency); if (mLeftIndents != null || mRightIndents != null) { builder.setIndents(mLeftIndents, mRightIndents); } @@ -6678,7 +6716,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Layout result = null; if (mText instanceof Spannable) { result = new DynamicLayout(mText, mTransformed, mTextPaint, wantWidth, - alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad, mBreakStrategy, + alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad, + mBreakStrategy, mHyphenationFrequency, getKeyListener() == null ? effectiveEllipsize : null, ellipsisWidth); } else { if (boring == UNKNOWN_BORING) { @@ -6726,7 +6765,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener .setTextDir(mTextDir) .setLineSpacing(mSpacingAdd, mSpacingMult) .setIncludePad(mIncludePad) - .setBreakStrategy(mBreakStrategy); + .setBreakStrategy(mBreakStrategy) + .setHyphenationFrequency(mHyphenationFrequency); if (mLeftIndents != null || mRightIndents != null) { builder.setIndents(mLeftIndents, mRightIndents); } @@ -7968,6 +8008,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * through a thunk. */ void sendAfterTextChanged(Editable text) { + sLastCutCopyOrTextChangedTime = 0; + if (mListeners != null) { final ArrayList<TextWatcher> list = mListeners; final int count = list.size(); @@ -8240,6 +8282,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public boolean onTouchEvent(MotionEvent event) { final int action = event.getActionMasked(); + if (mEditor != null && action == MotionEvent.ACTION_DOWN) { + // Detect double tap and inform the Editor. + if (mFirstTouch && (SystemClock.uptimeMillis() - mLastTouchUpTime) <= + ViewConfiguration.getDoubleTapTimeout()) { + mEditor.mDoubleTap = true; + mFirstTouch = false; + } else { + mEditor.mDoubleTap = false; + mFirstTouch = true; + } + } + + if (action == MotionEvent.ACTION_UP) { + mLastTouchUpTime = SystemClock.uptimeMillis(); + } + if (mEditor != null) { mEditor.onTouchEvent(event); @@ -9014,6 +9072,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener stopSelectionActionMode(); return true; + case ID_REPLACE: + if (mEditor != null) { + mEditor.replace(); + } + return true; + case ID_SHARE: shareSelectedText(); return true; @@ -9248,7 +9312,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } stopSelectionActionMode(); - LAST_CUT_OR_COPY_TIME = 0; + sLastCutCopyOrTextChangedTime = 0; } } @@ -9268,7 +9332,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener ClipboardManager clipboard = (ClipboardManager) getContext(). getSystemService(Context.CLIPBOARD_SERVICE); clipboard.setPrimaryClip(clip); - LAST_CUT_OR_COPY_TIME = SystemClock.uptimeMillis(); + sLastCutCopyOrTextChangedTime = SystemClock.uptimeMillis(); } /** diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 83fa967..ea18c12 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -29,8 +29,8 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.database.DataSetObserver; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -51,10 +51,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; import android.widget.AbsListView; import android.widget.BaseAdapter; -import android.widget.LinearLayout; import android.widget.ListView; import com.android.internal.R; @@ -74,6 +72,8 @@ public class ChooserActivity extends ResolverActivity { private IntentSender mRefinementIntentSender; private RefinementResultReceiver mRefinementResultReceiver; + private Intent mReferrerFillInIntent; + private ChooserListAdapter mChooserListAdapter; private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>(); @@ -81,7 +81,7 @@ public class ChooserActivity extends ResolverActivity { private static final int CHOOSER_TARGET_SERVICE_RESULT = 1; private static final int CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT = 2; - private Handler mTargetResultHandler = new Handler() { + private final Handler mChooserHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -176,6 +176,8 @@ public class ChooserActivity extends ResolverActivity { } } + mReferrerFillInIntent = new Intent().putExtra(Intent.EXTRA_REFERRER, getReferrer()); + mChosenComponentSender = intent.getParcelableExtra( Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER); mRefinementIntentSender = intent.getParcelableExtra( @@ -346,7 +348,7 @@ public class ChooserActivity extends ResolverActivity { if (!mServiceConnections.isEmpty()) { if (DEBUG) Log.d(TAG, "queryTargets setting watchdog timer for " + WATCHDOG_TIMEOUT_MILLIS + "ms"); - mTargetResultHandler.sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT, + mChooserHandler.sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT, WATCHDOG_TIMEOUT_MILLIS); } } @@ -379,7 +381,7 @@ public class ChooserActivity extends ResolverActivity { unbindService(conn); } mServiceConnections.clear(); - mTargetResultHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); + mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); } void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) { @@ -435,7 +437,7 @@ public class ChooserActivity extends ResolverActivity { private final ResolveInfo mBackupResolveInfo; private final ChooserTarget mChooserTarget; private Drawable mBadgeIcon = null; - private final Drawable mDisplayIcon; + private Drawable mDisplayIcon; private final Intent mFillInIntent; private final int mFillInFlags; @@ -451,7 +453,9 @@ public class ChooserActivity extends ResolverActivity { } } } - mDisplayIcon = new BitmapDrawable(getResources(), chooserTarget.getIcon()); + final Icon icon = chooserTarget.getIcon(); + // TODO do this in the background + mDisplayIcon = icon != null ? icon.loadDrawable(ChooserActivity.this) : null; if (sourceInfo != null) { mBackupResolveInfo = null; @@ -497,9 +501,12 @@ public class ChooserActivity extends ResolverActivity { ? mSourceInfo.getResolvedIntent() : getTargetIntent(); if (result == null) { Log.e(TAG, "ChooserTargetInfo#getFillInIntent: no fillIn intent available"); - } else if (mFillInIntent != null) { + } else { result = new Intent(result); - result.fillIn(mFillInIntent, mFillInFlags); + if (mFillInIntent != null) { + result.fillIn(mFillInIntent, mFillInFlags); + } + result.fillIn(mReferrerFillInIntent, 0); } return result; } @@ -867,7 +874,7 @@ public class ChooserActivity extends ResolverActivity { msg.what = CHOOSER_TARGET_SERVICE_RESULT; msg.obj = new ServiceResultInfo(mOriginalTarget, targets, ChooserTargetServiceConnection.this); - mTargetResultHandler.sendMessage(msg); + mChooserHandler.sendMessage(msg); } }; diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java index 80bc5fe..49230c1 100644 --- a/core/java/com/android/internal/app/PlatLogoActivity.java +++ b/core/java/com/android/internal/app/PlatLogoActivity.java @@ -45,33 +45,11 @@ import android.widget.FrameLayout; import android.widget.ImageView; public class PlatLogoActivity extends Activity { - final static int[] FLAVORS = { - 0xFF9C27B0, 0xFFBA68C8, // grape - 0xFFFF9800, 0xFFFFB74D, // orange - 0xFFF06292, 0xFFF8BBD0, // bubblegum - 0xFFAFB42B, 0xFFCDDC39, // lime - 0xFFFFEB3B, 0xFFFFF176, // lemon - 0xFF795548, 0xFFA1887F, // mystery flavor - }; FrameLayout mLayout; int mTapCount; int mKeyCount; PathInterpolator mInterpolator = new PathInterpolator(0f, 0f, 0.5f, 1f); - static int newColorIndex() { - return 2*((int) (Math.random()*FLAVORS.length/2)); - } - - Drawable makeRipple() { - final int idx = newColorIndex(); - final ShapeDrawable popbg = new ShapeDrawable(new OvalShape()); - popbg.getPaint().setColor(FLAVORS[idx]); - final RippleDrawable ripple = new RippleDrawable( - ColorStateList.valueOf(FLAVORS[idx+1]), - popbg, null); - return ripple; - } - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -87,120 +65,62 @@ public class PlatLogoActivity extends Activity { final int size = (int) (Math.min(Math.min(dm.widthPixels, dm.heightPixels), 600*dp) - 100*dp); - final View stick = new View(this) { - Paint mPaint = new Paint(); - Path mShadow = new Path(); - - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - setWillNotDraw(false); - setOutlineProvider(new ViewOutlineProvider() { - @Override - public void getOutline(View view, Outline outline) { - outline.setRect(0, getHeight() / 2, getWidth(), getHeight()); - } - }); - } + final View im = new View(this); + im.setTranslationZ(20); + im.setScaleX(0.5f); + im.setScaleY(0.5f); + im.setAlpha(0f); + im.setOutlineProvider(new ViewOutlineProvider() { @Override - public void onDraw(Canvas c) { - final int w = c.getWidth(); - final int h = c.getHeight() / 2; - c.translate(0, h); - final GradientDrawable g = new GradientDrawable(); - g.setOrientation(GradientDrawable.Orientation.LEFT_RIGHT); - g.setGradientCenter(w * 0.75f, 0); - g.setColors(new int[] { 0xFFFFFFFF, 0xFFAAAAAA }); - g.setBounds(0, 0, w, h); - g.draw(c); - mPaint.setColor(0xFFAAAAAA); - mShadow.reset(); - mShadow.moveTo(0,0); - mShadow.lineTo(w, 0); - mShadow.lineTo(w, size/2 + 1.5f*w); - mShadow.lineTo(0, size/2); - mShadow.close(); - c.drawPath(mShadow, mPaint); + public void getOutline(View view, Outline outline) { + final int pad = (int) (8*dp); + outline.setOval(pad, pad, view.getWidth()-pad, view.getHeight()-pad); } - }; - mLayout.addView(stick, new FrameLayout.LayoutParams((int) (32 * dp), - ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER_HORIZONTAL)); - stick.setAlpha(0f); - - final ImageView im = new ImageView(this); - im.setTranslationZ(20); - im.setScaleX(0); - im.setScaleY(0); + }); final Drawable platlogo = getDrawable(com.android.internal.R.drawable.platlogo); - platlogo.setAlpha(0); - im.setImageDrawable(platlogo); - im.setBackground(makeRipple()); + im.setBackground(new RippleDrawable( + ColorStateList.valueOf(0xFFFFFFFF), + platlogo, + null)); im.setClickable(true); - final ShapeDrawable highlight = new ShapeDrawable(new OvalShape()); - highlight.getPaint().setColor(0x10FFFFFF); - highlight.setBounds((int)(size*.15f), (int)(size*.15f), - (int)(size*.6f), (int)(size*.6f)); - im.getOverlay().add(highlight); im.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if (mTapCount == 0) { - im.animate() - .translationZ(40) - .scaleX(1) - .scaleY(1) - .setInterpolator(mInterpolator) - .setDuration(700) - .setStartDelay(500) - .start(); - - final ObjectAnimator a = ObjectAnimator.ofInt(platlogo, "alpha", 0, 255); - a.setInterpolator(mInterpolator); - a.setStartDelay(1000); - a.start(); - - stick.animate() - .translationZ(20) - .alpha(1) - .setInterpolator(mInterpolator) - .setDuration(700) - .setStartDelay(750) - .start(); - - im.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (mTapCount < 5) return false; - - final ContentResolver cr = getContentResolver(); - if (Settings.System.getLong(cr, Settings.System.EGG_MODE, 0) - == 0) { - // For posterity: the moment this user unlocked the easter egg + im.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (mTapCount < 5) return false; + + final ContentResolver cr = getContentResolver(); + if (Settings.System.getLong(cr, Settings.System.EGG_MODE, 0) + == 0) { + // For posterity: the moment this user unlocked the easter egg + try { Settings.System.putLong(cr, Settings.System.EGG_MODE, System.currentTimeMillis()); + } catch (RuntimeException e) { + Log.e("PlatLogoActivity", "Can't write settings", e); } - im.post(new Runnable() { - @Override - public void run() { - try { - startActivity(new Intent(Intent.ACTION_MAIN) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_CLEAR_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) - .addCategory("com.android.internal.category.PLATLOGO")); - } catch (ActivityNotFoundException ex) { - Log.e("PlatLogoActivity", "No more eggs."); - } - finish(); - } - }); - return true; } - }); - } else { - im.setBackground(makeRipple()); - } + im.post(new Runnable() { + @Override + public void run() { + try { + startActivity(new Intent(Intent.ACTION_MAIN) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_CLEAR_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) + .addCategory("com.android.internal.category.PLATLOGO")); + } catch (ActivityNotFoundException ex) { + Log.e("PlatLogoActivity", "No more eggs."); + } + finish(); + } + }); + return true; + } + }); mTapCount++; } }); @@ -229,7 +149,7 @@ public class PlatLogoActivity extends Activity { mLayout.addView(im, new FrameLayout.LayoutParams(size, size, Gravity.CENTER)); - im.animate().scaleX(0.3f).scaleY(0.3f) + im.animate().scaleX(1f).scaleY(1f).alpha(1f) .setInterpolator(mInterpolator) .setDuration(500) .setStartDelay(800) diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 26cdf6b..4696757 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -668,29 +668,37 @@ public class ResolverActivity extends Activity { if (r.match > bestMatch) bestMatch = r.match; } if (alwaysCheck) { - PackageManager pm = getPackageManager(); + final int userId = getUserId(); + final PackageManager pm = getPackageManager(); // Set the preferred Activity pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent()); - // Update Domain Verification status - int userId = getUserId(); - ComponentName cn = intent.getComponent(); - String packageName = cn.getPackageName(); - String dataScheme = (data != null) ? data.getScheme() : null; - - boolean isHttpOrHttps = (dataScheme != null) && - (dataScheme.equals(IntentFilter.SCHEME_HTTP) || - dataScheme.equals(IntentFilter.SCHEME_HTTPS)); - - boolean isViewAction = (action != null) && action.equals(Intent.ACTION_VIEW); - boolean hasCategoryBrowsable = (categories != null) && - categories.contains(Intent.CATEGORY_BROWSABLE); - - if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) { - pm.updateIntentVerificationStatus(packageName, - PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, - userId); + if (ri.handleAllWebDataURI) { + // Set default Browser if needed + final String packageName = pm.getDefaultBrowserPackageName(userId); + if (TextUtils.isEmpty(packageName)) { + pm.setDefaultBrowserPackageName(ri.activityInfo.packageName, userId); + } + } else { + // Update Domain Verification status + ComponentName cn = intent.getComponent(); + String packageName = cn.getPackageName(); + String dataScheme = (data != null) ? data.getScheme() : null; + + boolean isHttpOrHttps = (dataScheme != null) && + (dataScheme.equals(IntentFilter.SCHEME_HTTP) || + dataScheme.equals(IntentFilter.SCHEME_HTTPS)); + + boolean isViewAction = (action != null) && action.equals(Intent.ACTION_VIEW); + boolean hasCategoryBrowsable = (categories != null) && + categories.contains(Intent.CATEGORY_BROWSABLE); + + if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) { + pm.updateIntentVerificationStatus(packageName, + PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, + userId); + } } } else { try { diff --git a/core/java/com/android/internal/logging/MetricsConstants.java b/core/java/com/android/internal/logging/MetricsConstants.java index 6aa81ce..65dc743 100644 --- a/core/java/com/android/internal/logging/MetricsConstants.java +++ b/core/java/com/android/internal/logging/MetricsConstants.java @@ -21,7 +21,9 @@ package com.android.internal.logging; * @hide */ public interface MetricsConstants { - // These constants must match those in the analytic pipeline. + // These constants must match those in the analytic pipeline, do not edit. + public static final int VIEW_UNKNOWN = 0; + public static final int MAIN_SETTINGS = 1; public static final int ACCESSIBILITY = 2; public static final int ACCESSIBILITY_CAPTION_PROPERTIES = 3; public static final int ACCESSIBILITY_SERVICE = 4; @@ -32,16 +34,11 @@ public interface MetricsConstants { public static final int ACCOUNTS_ACCOUNT_SYNC = 9; public static final int ACCOUNTS_CHOOSE_ACCOUNT_ACTIVITY = 10; public static final int ACCOUNTS_MANAGE_ACCOUNTS = 11; - public static final int ACTION_WIFI_ADD_NETWORK = 134; - public static final int ACTION_WIFI_CONNECT = 135; - public static final int ACTION_WIFI_FORCE_SCAN = 136; - public static final int ACTION_WIFI_FORGET = 137; - public static final int ACTION_WIFI_OFF = 138; - public static final int ACTION_WIFI_ON = 139; public static final int APN = 12; public static final int APN_EDITOR = 13; + public static final int APP_OPS_DETAILS = 14; + public static final int APP_OPS_SUMMARY = 15; public static final int APPLICATION = 16; - public static final int APPLICATIONS_ADVANCED = 130; public static final int APPLICATIONS_APP_LAUNCH = 17; public static final int APPLICATIONS_APP_PERMISSION = 18; public static final int APPLICATIONS_APP_STORAGE = 19; @@ -49,8 +46,6 @@ public interface MetricsConstants { public static final int APPLICATIONS_PROCESS_STATS_DETAIL = 21; public static final int APPLICATIONS_PROCESS_STATS_MEM_DETAIL = 22; public static final int APPLICATIONS_PROCESS_STATS_UI = 23; - public static final int APP_OPS_DETAILS = 14; - public static final int APP_OPS_SUMMARY = 15; public static final int BLUETOOTH = 24; public static final int BLUETOOTH_DEVICE_PICKER = 25; public static final int BLUETOOTH_DEVICE_PROFILES = 26; @@ -69,9 +64,7 @@ public interface MetricsConstants { public static final int DEVELOPMENT = 39; public static final int DEVICEINFO = 40; public static final int DEVICEINFO_IMEI_INFORMATION = 41; - @Deprecated public static final int DEVICEINFO_MEMORY = 42; - public static final int DEVICEINFO_STORAGE = 42; public static final int DEVICEINFO_SIM_STATUS = 43; public static final int DEVICEINFO_STATUS = 44; public static final int DEVICEINFO_USB = 45; @@ -86,21 +79,15 @@ public interface MetricsConstants { public static final int FUELGAUGE_POWER_USAGE_SUMMARY = 54; public static final int HOME = 55; public static final int ICC_LOCK = 56; - public static final int INPUTMETHOD_KEYBOARD = 58; public static final int INPUTMETHOD_LANGUAGE = 57; + public static final int INPUTMETHOD_KEYBOARD = 58; public static final int INPUTMETHOD_SPELL_CHECKERS = 59; public static final int INPUTMETHOD_SUBTYPE_ENABLER = 60; public static final int INPUTMETHOD_USER_DICTIONARY = 61; public static final int INPUTMETHOD_USER_DICTIONARY_ADD_WORD = 62; public static final int LOCATION = 63; public static final int LOCATION_MODE = 64; - public static final int LOCATION_SCANNING = 131; - public static final int MAIN_SETTINGS = 1; public static final int MANAGE_APPLICATIONS = 65; - public static final int MANAGE_APPLICATIONS_ALL = 132; - public static final int MANAGE_APPLICATIONS_NOTIFICATIONS = 133; - public static final int MANAGE_DOMAIN_URLS = 143; - public static final int MANAGE_PERMISSIONS = 140; public static final int MASTER_CLEAR = 66; public static final int MASTER_CLEAR_CONFIRM = 67; public static final int NET_DATA_USAGE_METERED = 68; @@ -108,37 +95,16 @@ public interface MetricsConstants { public static final int NFC_PAYMENT = 70; public static final int NOTIFICATION = 71; public static final int NOTIFICATION_APP_NOTIFICATION = 72; - public static final int NOTIFICATION_ITEM = 128; - public static final int NOTIFICATION_ITEM_ACTION = 129; public static final int NOTIFICATION_OTHER_SOUND = 73; - public static final int NOTIFICATION_PANEL = 127; public static final int NOTIFICATION_REDACTION = 74; public static final int NOTIFICATION_STATION = 75; public static final int NOTIFICATION_ZEN_MODE = 76; - public static final int NOTIFICATION_ZEN_MODE_AUTOMATION = 142; - public static final int NOTIFICATION_ZEN_MODE_PRIORITY = 141; public static final int OWNER_INFO = 77; public static final int PRINT_JOB_SETTINGS = 78; public static final int PRINT_SERVICE_SETTINGS = 79; public static final int PRINT_SETTINGS = 80; public static final int PRIVACY = 81; public static final int PROXY_SELECTOR = 82; - public static final int QS_AIRPLANEMODE = 112; - public static final int QS_BLUETOOTH = 113; - public static final int QS_CAST = 114; - public static final int QS_CELLULAR = 115; - public static final int QS_COLORINVERSION = 116; - public static final int QS_DATAUSAGEDETAIL = 117; - public static final int QS_DND = 118; - public static final int QS_FLASHLIGHT = 119; - public static final int QS_HOTSPOT = 120; - public static final int QS_INTENT = 121; - public static final int QS_LOCATION = 122; - public static final int QS_PANEL = 111; - public static final int QS_ROTATIONLOCK = 123; - public static final int QS_USERDETAIL = 125; - public static final int QS_USERDETAILITE = 124; - public static final int QS_WIFI = 126; public static final int RESET_NETWORK = 83; public static final int RESET_NETWORK_CONFIRM = 84; public static final int RUNNING_SERVICE_DETAILS = 85; @@ -147,25 +113,102 @@ public interface MetricsConstants { public static final int SIM = 88; public static final int TESTING = 89; public static final int TETHER = 90; - public static final int TRUSTED_CREDENTIALS = 92; public static final int TRUST_AGENT = 91; + public static final int TRUSTED_CREDENTIALS = 92; public static final int TTS_ENGINE_SETTINGS = 93; public static final int TTS_TEXT_TO_SPEECH = 94; public static final int USAGE_ACCESS = 95; public static final int USER = 96; public static final int USERS_APP_RESTRICTIONS = 97; public static final int USER_DETAILS = 98; - public static final int VIEW_UNKNOWN = 0; public static final int VOICE_INPUT = 99; public static final int VPN = 100; public static final int WALLPAPER_TYPE = 101; public static final int WFD_WIFI_DISPLAY = 102; public static final int WIFI = 103; public static final int WIFI_ADVANCED = 104; - public static final int WIFI_APITEST = 107; public static final int WIFI_CALLING = 105; + public static final int WIFI_SAVED_ACCESS_POINTS = 106; + public static final int WIFI_APITEST = 107; public static final int WIFI_INFO = 108; public static final int WIFI_P2P = 109; - public static final int WIFI_SAVED_ACCESS_POINTS = 106; public static final int WIRELESS = 110; + public static final int QS_PANEL = 111; + public static final int QS_AIRPLANEMODE = 112; + public static final int QS_BLUETOOTH = 113; + public static final int QS_CAST = 114; + public static final int QS_CELLULAR = 115; + public static final int QS_COLORINVERSION = 116; + public static final int QS_DATAUSAGEDETAIL = 117; + public static final int QS_DND = 118; + public static final int QS_FLASHLIGHT = 119; + public static final int QS_HOTSPOT = 120; + public static final int QS_INTENT = 121; + public static final int QS_LOCATION = 122; + public static final int QS_ROTATIONLOCK = 123; + public static final int QS_USERDETAILITE = 124; + public static final int QS_USERDETAIL = 125; + public static final int QS_WIFI = 126; + public static final int NOTIFICATION_PANEL = 127; + public static final int NOTIFICATION_ITEM = 128; + public static final int NOTIFICATION_ITEM_ACTION = 129; + public static final int APPLICATIONS_ADVANCED = 130; + public static final int LOCATION_SCANNING = 131; + public static final int MANAGE_APPLICATIONS_ALL = 132; + public static final int MANAGE_APPLICATIONS_NOTIFICATIONS = 133; + public static final int ACTION_WIFI_ADD_NETWORK = 134; + public static final int ACTION_WIFI_CONNECT = 135; + public static final int ACTION_WIFI_FORCE_SCAN = 136; + public static final int ACTION_WIFI_FORGET = 137; + public static final int ACTION_WIFI_OFF = 138; + public static final int ACTION_WIFI_ON = 139; + public static final int MANAGE_PERMISSIONS = 140; + public static final int NOTIFICATION_ZEN_MODE_PRIORITY = 141; + public static final int NOTIFICATION_ZEN_MODE_AUTOMATION = 142; + public static final int MANAGE_DOMAIN_URLS = 143; + public static final int NOTIFICATION_ZEN_MODE_SCHEDULE_RULE = 144; + public static final int NOTIFICATION_ZEN_MODE_EXTERNAL_RULE = 145; + public static final int NOTIFICATION_ZEN_MODE_EVENT_RULE = 146; + public static final int ACTION_BAN_APP_NOTES = 147; + public static final int ACTION_DISMISS_ALL_NOTES = 148; + public static final int QS_DND_DETAILS = 149; + public static final int QS_BLUETOOTH_DETAILS = 150; + public static final int QS_CAST_DETAILS = 151; + public static final int QS_WIFI_DETAILS = 152; + public static final int QS_WIFI_TOGGLE = 153; + public static final int QS_BLUETOOTH_TOGGLE = 154; + public static final int QS_CELLULAR_TOGGLE = 155; + public static final int QS_SWITCH_USER = 156; + public static final int QS_CAST_SELECT = 157; + public static final int QS_CAST_DISCONNECT = 158; + public static final int ACTION_BLUETOOTH_TOGGLE = 159; + public static final int ACTION_BLUETOOTH_SCAN = 160; + public static final int ACTION_BLUETOOTH_RENAME = 161; + public static final int ACTION_BLUETOOTH_FILES = 162; + public static final int QS_DND_TIME = 163; + public static final int QS_DND_CONDITION_SELECT = 164; + public static final int QS_DND_ZEN_SELECT = 165; + public static final int QS_DND_TOGGLE = 166; + public static final int ACTION_ZEN_ALLOW_REMINDERS = 167; + public static final int ACTION_ZEN_ALLOW_EVENTS = 168; + public static final int ACTION_ZEN_ALLOW_MESSAGES = 169; + public static final int ACTION_ZEN_ALLOW_CALLS = 170; + public static final int ACTION_ZEN_ALLOW_REPEAT_CALLS = 171; + public static final int ACTION_ZEN_ADD_RULE = 172; + public static final int ACTION_ZEN_ADD_RULE_OK = 173; + public static final int ACTION_ZEN_DELETE_RULE = 174; + public static final int ACTION_ZEN_DELETE_RULE_OK = 175; + public static final int ACTION_ZEN_ENABLE_RULE = 176; + public static final int ACTION_AIRPLANE_TOGGLE = 177; + public static final int ACTION_CELL_DATA_TOGGLE = 178; + public static final int NOTIFICATION_ACCESS = 179; + public static final int NOTIFICATION_ZEN_MODE_ACCESS = 180; + public static final int APPLICATIONS_DEFAULT_APPS = 181; + public static final int APPLICATIONS_STORAGE_APPS = 182; + public static final int APPLICATIONS_USAGE_ACCESS_DETAIL = 183; + public static final int APPLICATIONS_HIGH_POWER_APPS = 184; + public static final int FUELGAUGE_HIGH_POWER_DETAILS = 185; + + //aliases + public static final int DEVICEINFO_STORAGE = DEVICEINFO_MEMORY; } diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java index cf25cef..230d96d 100644 --- a/core/java/com/android/internal/logging/MetricsLogger.java +++ b/core/java/com/android/internal/logging/MetricsLogger.java @@ -26,45 +26,7 @@ import android.view.View; * @hide */ public class MetricsLogger implements MetricsConstants { - // These constants are temporary, they should migrate to MetricsConstants. - - public static final int NOTIFICATION_ZEN_MODE_SCHEDULE_RULE = 144; - public static final int NOTIFICATION_ZEN_MODE_EXTERNAL_RULE = 145; - public static final int ACTION_BAN_APP_NOTES = 146; - public static final int NOTIFICATION_ZEN_MODE_EVENT_RULE = 147; - public static final int ACTION_DISMISS_ALL_NOTES = 148; - public static final int QS_DND_DETAILS = 149; - public static final int QS_BLUETOOTH_DETAILS = 150; - public static final int QS_CAST_DETAILS = 151; - public static final int QS_WIFI_DETAILS = 152; - public static final int QS_WIFI_TOGGLE = 153; - public static final int QS_BLUETOOTH_TOGGLE = 154; - public static final int QS_CELLULAR_TOGGLE = 155; - public static final int QS_SWITCH_USER = 156; - public static final int QS_CAST_SELECT = 157; - public static final int QS_CAST_DISCONNECT = 158; - public static final int ACTION_BLUETOOTH_TOGGLE = 159; - public static final int ACTION_BLUETOOTH_SCAN = 160; - public static final int ACTION_BLUETOOTH_RENAME = 161; - public static final int ACTION_BLUETOOTH_FILES = 162; - public static final int QS_DND_TIME = 163; - public static final int QS_DND_CONDITION_SELECT = 164; - public static final int QS_DND_ZEN_SELECT = 165; - public static final int QS_DND_TOGGLE = 166; - public static final int ACTION_ZEN_ALLOW_REMINDERS = 167; - public static final int ACTION_ZEN_ALLOW_EVENTS = 168; - public static final int ACTION_ZEN_ALLOW_MESSAGES = 169; - public static final int ACTION_ZEN_ALLOW_CALLS = 170; - public static final int ACTION_ZEN_ALLOW_REPEAT_CALLS = 171; - public static final int ACTION_ZEN_ADD_RULE = 172; - public static final int ACTION_ZEN_ADD_RULE_OK = 173; - public static final int ACTION_ZEN_DELETE_RULE = 174; - public static final int ACTION_ZEN_DELETE_RULE_OK = 175; - public static final int ACTION_ZEN_ENABLE_RULE = 176; - public static final int ACTION_AIRPLANE_TOGGLE = 177; - public static final int ACTION_CELL_DATA_TOGGLE = 178; - public static final int NOTIFICATION_ACCESS = 179; - public static final int NOTIFICATION_ZEN_MODE_ACCESS = 180; + // Temporary constants go here, to await migration to MetricsConstants. public static void visible(Context context, int category) throws IllegalArgumentException { if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) { diff --git a/core/java/com/android/internal/midi/MidiDispatcher.java b/core/java/com/android/internal/midi/MidiDispatcher.java index 70e699a..1a3c37c 100644 --- a/core/java/com/android/internal/midi/MidiDispatcher.java +++ b/core/java/com/android/internal/midi/MidiDispatcher.java @@ -27,7 +27,7 @@ import java.util.concurrent.CopyOnWriteArrayList; * This class subclasses {@link android.media.midi.MidiReceiver} and dispatches any data it receives * to its receiver list. Any receivers that throw an exception upon receiving data will * be automatically removed from the receiver list, but no IOException will be returned - * from the dispatcher's {@link android.media.midi.MidiReceiver#onReceive} in that case. + * from the dispatcher's {@link android.media.midi.MidiReceiver#onSend} in that case. */ public final class MidiDispatcher extends MidiReceiver { @@ -35,21 +35,13 @@ public final class MidiDispatcher extends MidiReceiver { = new CopyOnWriteArrayList<MidiReceiver>(); private final MidiSender mSender = new MidiSender() { - /** - * Called to connect a {@link android.media.midi.MidiReceiver} to the sender - * - * @param receiver the receiver to connect - */ - public void connect(MidiReceiver receiver) { + @Override + public void onConnect(MidiReceiver receiver) { mReceivers.add(receiver); } - /** - * Called to disconnect a {@link android.media.midi.MidiReceiver} from the sender - * - * @param receiver the receiver to disconnect - */ - public void disconnect(MidiReceiver receiver) { + @Override + public void onDisconnect(MidiReceiver receiver) { mReceivers.remove(receiver); } }; @@ -73,10 +65,10 @@ public final class MidiDispatcher extends MidiReceiver { } @Override - public void onReceive(byte[] msg, int offset, int count, long timestamp) throws IOException { + public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException { for (MidiReceiver receiver : mReceivers) { try { - receiver.sendWithTimestamp(msg, offset, count, timestamp); + receiver.send(msg, offset, count, timestamp); } catch (IOException e) { // if the receiver fails we remove the receiver but do not propagate the exception mReceivers.remove(receiver); @@ -85,7 +77,7 @@ public final class MidiDispatcher extends MidiReceiver { } @Override - public void flush() throws IOException { + public void onFlush() throws IOException { for (MidiReceiver receiver : mReceivers) { receiver.flush(); } diff --git a/core/java/com/android/internal/midi/MidiEventScheduler.java b/core/java/com/android/internal/midi/MidiEventScheduler.java index 4dc5838..7b01904 100644 --- a/core/java/com/android/internal/midi/MidiEventScheduler.java +++ b/core/java/com/android/internal/midi/MidiEventScheduler.java @@ -36,7 +36,7 @@ public class MidiEventScheduler extends EventScheduler { * time. */ @Override - public void onReceive(byte[] msg, int offset, int count, long timestamp) + public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException { MidiEvent event = createScheduledEvent(msg, offset, count, timestamp); if (event != null) { @@ -45,7 +45,7 @@ public class MidiEventScheduler extends EventScheduler { } @Override - public void flush() { + public void onFlush() { MidiEventScheduler.this.flush(); } } diff --git a/core/java/com/android/internal/midi/MidiFramer.java b/core/java/com/android/internal/midi/MidiFramer.java index 8ed5b5a..058f57c 100644 --- a/core/java/com/android/internal/midi/MidiFramer.java +++ b/core/java/com/android/internal/midi/MidiFramer.java @@ -54,10 +54,10 @@ public class MidiFramer extends MidiReceiver { } /* - * @see android.midi.MidiReceiver#onReceive(byte[], int, int, long) + * @see android.midi.MidiReceiver#onSend(byte[], int, int, long) */ @Override - public void onReceive(byte[] data, int offset, int count, long timestamp) + public void onSend(byte[] data, int offset, int count, long timestamp) throws IOException { // Log.i(TAG, formatMidiData(data, offset, count)); int sysExStartOffset = (mInSysEx ? offset : -1); @@ -77,7 +77,7 @@ public class MidiFramer extends MidiReceiver { } else if (b == 0xF7 /* SysEx End */) { // Log.i(TAG, "SysEx End"); if (mInSysEx) { - mReceiver.sendWithTimestamp(data, sysExStartOffset, + mReceiver.send(data, sysExStartOffset, offset - sysExStartOffset + 1, timestamp); mInSysEx = false; sysExStartOffset = -1; @@ -91,11 +91,11 @@ public class MidiFramer extends MidiReceiver { } else { // real-time? // Single byte message interleaved with other data. if (mInSysEx) { - mReceiver.sendWithTimestamp(data, sysExStartOffset, + mReceiver.send(data, sysExStartOffset, offset - sysExStartOffset, timestamp); sysExStartOffset = offset + 1; } - mReceiver.sendWithTimestamp(data, offset, 1, timestamp); + mReceiver.send(data, offset, 1, timestamp); } } else { // data byte // Save SysEx data for SysEx End marker or end of buffer. @@ -105,7 +105,7 @@ public class MidiFramer extends MidiReceiver { if (mRunningStatus != 0) { mBuffer[0] = (byte) mRunningStatus; } - mReceiver.sendWithTimestamp(mBuffer, 0, mCount, timestamp); + mReceiver.send(mBuffer, 0, mCount, timestamp); mNeeded = MidiConstants.getBytesPerMessage(mBuffer[0]) - 1; mCount = 1; } @@ -116,7 +116,7 @@ public class MidiFramer extends MidiReceiver { // send any accumulatedSysEx data if (sysExStartOffset >= 0 && sysExStartOffset < offset) { - mReceiver.sendWithTimestamp(data, sysExStartOffset, + mReceiver.send(data, sysExStartOffset, offset - sysExStartOffset, timestamp); } } diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index a53d46c..fbe87c5 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -615,6 +615,7 @@ public final class BatteryStatsHelper { mStatsType); if (bs.sumPower() > 0) { aggregateSippers(bs, mBluetoothSippers, "Bluetooth"); + mUsageList.add(bs); } } @@ -742,7 +743,6 @@ public final class BatteryStatsHelper { parcel.setDataPosition(0); BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR .createFromParcel(parcel); - stats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED); return stats; } catch (IOException e) { Log.w(TAG, "Unable to read statistics stream", e); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 405c861..eaca43b 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -78,6 +78,7 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; @@ -104,7 +105,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 125 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 126 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -131,6 +132,9 @@ public final class BatteryStatsImpl extends BatteryStats { private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader(); private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats(); + private final KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader(); + private final KernelCpuSpeedReader mKernelCpuSpeedReader = new KernelCpuSpeedReader(); + public interface BatteryCallback { public void batteryNeedsCpuUpdate(); public void batteryPowerChanged(boolean onBattery); @@ -302,9 +306,6 @@ public final class BatteryStatsImpl extends BatteryStats { String mStartPlatformVersion; String mEndPlatformVersion; - long mLastRecordedClockTime; - long mLastRecordedClockRealtime; - long mUptime; long mUptimeStart; long mRealtime; @@ -796,20 +797,6 @@ public final class BatteryStatsImpl extends BatteryStats { } } - public static class SamplingCounter extends Counter { - SamplingCounter(TimeBase timeBase, Parcel in) { - super(timeBase, in); - } - - SamplingCounter(TimeBase timeBase) { - super(timeBase); - } - - public void addCountAtomic(long count) { - mCount.addAndGet((int)count); - } - } - public static class LongSamplingCounter extends LongCounter implements TimeBaseObs { final TimeBase mTimeBase; long mCount; @@ -2329,8 +2316,6 @@ public final class BatteryStatsImpl extends BatteryStats { if (dataSize == 0) { // The history is currently empty; we need it to start with a time stamp. cur.currentTime = System.currentTimeMillis(); - mLastRecordedClockTime = cur.currentTime; - mLastRecordedClockRealtime = elapsedRealtimeMs; addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_RESET, cur); } addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur); @@ -2503,8 +2488,6 @@ public final class BatteryStatsImpl extends BatteryStats { mHistoryOverflow = false; mActiveHistoryStates = 0xffffffff; mActiveHistoryStates2 = 0xffffffff; - mLastRecordedClockTime = 0; - mLastRecordedClockRealtime = 0; } public void updateTimeBasesLocked(boolean unplugged, boolean screenOff, long uptime, @@ -2554,18 +2537,6 @@ public final class BatteryStatsImpl extends BatteryStats { final long currentTime = System.currentTimeMillis(); final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); - if (isStartClockTimeValid()) { - // Has the time changed sufficiently that it is really worth recording? - if (mLastRecordedClockTime != 0) { - long expectedClockTime = mLastRecordedClockTime - + (elapsedRealtime - mLastRecordedClockRealtime); - if (currentTime >= (expectedClockTime-500) - && currentTime <= (expectedClockTime+500)) { - // Not sufficiently changed, skip! - return; - } - } - } recordCurrentTimeChangeLocked(currentTime, elapsedRealtime, uptime); if (isStartClockTimeValid()) { mStartClockTime = currentTime; @@ -2936,8 +2907,7 @@ public final class BatteryStatsImpl extends BatteryStats { public void finishAddingCpuLocked(int perc, int remainUTime, int remainSTtime, int totalUTime, int totalSTime, int statUserTime, int statSystemTime, - int statIOWaitTime, int statIrqTime, int statSoftIrqTime, int statIdleTime, - long[] cpuSpeedTimes) { + int statIOWaitTime, int statIrqTime, int statSoftIrqTime, int statIdleTime) { if (DEBUG) Slog.d(TAG, "Adding cpu: tuser=" + totalUTime + " tsys=" + totalSTime + " user=" + statUserTime + " sys=" + statSystemTime + " io=" + statIOWaitTime + " irq=" + statIrqTime @@ -2977,7 +2947,7 @@ public final class BatteryStatsImpl extends BatteryStats { remainSTtime -= mySTime; num--; Uid.Proc proc = uid.getProcessStatsLocked("*wakelock*"); - proc.addCpuTimeLocked(myUTime, mySTime, cpuSpeedTimes); + proc.addCpuTimeLocked(myUTime, mySTime); } } } @@ -2988,7 +2958,7 @@ public final class BatteryStatsImpl extends BatteryStats { Uid uid = getUidStatsLocked(Process.SYSTEM_UID); if (uid != null) { Uid.Proc proc = uid.getProcessStatsLocked("*lost*"); - proc.addCpuTimeLocked(remainUTime, remainSTtime, cpuSpeedTimes); + proc.addCpuTimeLocked(remainUTime, remainSTtime); } } } @@ -4416,6 +4386,10 @@ public final class BatteryStatsImpl extends BatteryStats { long mCurStepUserTime; long mCurStepSystemTime; + LongSamplingCounter mUserCpuTime = new LongSamplingCounter(mOnBatteryTimeBase); + LongSamplingCounter mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase); + LongSamplingCounter[] mSpeedBins; + /** * The statistics we have collected for this uid's wake locks. */ @@ -4473,6 +4447,7 @@ public final class BatteryStatsImpl extends BatteryStats { mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED, mWifiMulticastTimers, mOnBatteryTimeBase); mProcessStateTimer = new StopwatchTimer[NUM_PROCESS_STATE]; + mSpeedBins = new LongSamplingCounter[getCpuSpeedSteps()]; } @Override @@ -4943,6 +4918,26 @@ public final class BatteryStatsImpl extends BatteryStats { } @Override + public long getUserCpuTimeUs(int which) { + return mUserCpuTime.getCountLocked(which); + } + + @Override + public long getSystemCpuTimeUs(int which) { + return mSystemCpuTime.getCountLocked(which); + } + + @Override + public long getTimeAtCpuSpeed(int step, int which) { + if (step >= 0 && step < mSpeedBins.length) { + if (mSpeedBins[step] != null) { + return mSpeedBins[step].getCountLocked(which); + } + } + return 0; + } + + @Override public long getWifiControllerActivity(int type, int which) { if (type >= 0 && type < NUM_CONTROLLER_ACTIVITY_TYPES && mWifiControllerTime[type] != null) { @@ -5044,6 +5039,15 @@ public final class BatteryStatsImpl extends BatteryStats { } } + mUserCpuTime.reset(false); + mSystemCpuTime.reset(false); + for (int i = 0; i < mSpeedBins.length; i++) { + LongSamplingCounter c = mSpeedBins[i]; + if (c != null) { + c.reset(false); + } + } + final ArrayMap<String, Wakelock> wakeStats = mWakelockStats.getMap(); for (int iw=wakeStats.size()-1; iw>=0; iw--) { Wakelock wl = wakeStats.valueAt(iw); @@ -5177,6 +5181,15 @@ public final class BatteryStatsImpl extends BatteryStats { } } mPids.clear(); + + mUserCpuTime.detach(); + mSystemCpuTime.detach(); + for (int i = 0; i < mSpeedBins.length; i++) { + LongSamplingCounter c = mSpeedBins[i]; + if (c != null) { + c.detach(); + } + } } return !active; @@ -5335,6 +5348,20 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(0); } } + + mUserCpuTime.writeToParcel(out); + mSystemCpuTime.writeToParcel(out); + + out.writeInt(mSpeedBins.length); + for (int i = 0; i < mSpeedBins.length; i++) { + LongSamplingCounter c = mSpeedBins[i]; + if (c != null) { + out.writeInt(1); + c.writeToParcel(out); + } else { + out.writeInt(0); + } + } } void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) { @@ -5500,6 +5527,18 @@ public final class BatteryStatsImpl extends BatteryStats { mBluetoothControllerTime[i] = null; } } + + mUserCpuTime = new LongSamplingCounter(mOnBatteryTimeBase, in); + mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase, in); + + int bins = in.readInt(); + int steps = getCpuSpeedSteps(); + mSpeedBins = new LongSamplingCounter[bins >= steps ? bins : steps]; + for (int i = 0; i < bins; i++) { + if (in.readInt() != 0) { + mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase, in); + } + } } /** @@ -5780,14 +5819,11 @@ public final class BatteryStatsImpl extends BatteryStats { */ int mProcessState = PROCESS_STATE_NONE; - SamplingCounter[] mSpeedBins; - ArrayList<ExcessivePower> mExcessivePower; Proc(String name) { mName = name; mOnBatteryTimeBase.add(this); - mSpeedBins = new SamplingCounter[getCpuSpeedSteps()]; } public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) { @@ -5809,25 +5845,12 @@ public final class BatteryStatsImpl extends BatteryStats { mLoadedStarts = mLoadedNumCrashes = mLoadedNumAnrs = 0; mUnpluggedUserTime = mUnpluggedSystemTime = mUnpluggedForegroundTime = 0; mUnpluggedStarts = mUnpluggedNumCrashes = mUnpluggedNumAnrs = 0; - for (int i = 0; i < mSpeedBins.length; i++) { - SamplingCounter c = mSpeedBins[i]; - if (c != null) { - c.reset(false); - } - } mExcessivePower = null; } void detach() { mActive = false; mOnBatteryTimeBase.remove(this); - for (int i = 0; i < mSpeedBins.length; i++) { - SamplingCounter c = mSpeedBins[i]; - if (c != null) { - mOnBatteryTimeBase.remove(c); - mSpeedBins[i] = null; - } - } } public int countExcessivePowers() { @@ -5921,18 +5944,6 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(mUnpluggedStarts); out.writeInt(mUnpluggedNumCrashes); out.writeInt(mUnpluggedNumAnrs); - - out.writeInt(mSpeedBins.length); - for (int i = 0; i < mSpeedBins.length; i++) { - SamplingCounter c = mSpeedBins[i]; - if (c != null) { - out.writeInt(1); - c.writeToParcel(out); - } else { - out.writeInt(0); - } - } - writeExcessivePowerToParcelLocked(out); } @@ -5955,39 +5966,12 @@ public final class BatteryStatsImpl extends BatteryStats { mUnpluggedStarts = in.readInt(); mUnpluggedNumCrashes = in.readInt(); mUnpluggedNumAnrs = in.readInt(); - - int bins = in.readInt(); - int steps = getCpuSpeedSteps(); - mSpeedBins = new SamplingCounter[bins >= steps ? bins : steps]; - for (int i = 0; i < bins; i++) { - if (in.readInt() != 0) { - mSpeedBins[i] = new SamplingCounter(mOnBatteryTimeBase, in); - } - } - readExcessivePowerFromParcelLocked(in); } - public BatteryStatsImpl getBatteryStats() { - return BatteryStatsImpl.this; - } - - public void addCpuTimeLocked(int utime, int stime, long[] speedStepBins) { + public void addCpuTimeLocked(int utime, int stime) { mUserTime += utime; - mCurStepUserTime += utime; mSystemTime += stime; - mCurStepSystemTime += stime; - - for (int i = 0; i < mSpeedBins.length && i < speedStepBins.length; i++) { - long amt = speedStepBins[i]; - if (amt != 0) { - SamplingCounter c = mSpeedBins[i]; - if (c == null) { - mSpeedBins[i] = c = new SamplingCounter(mOnBatteryTimeBase); - } - c.addCountAtomic(speedStepBins[i]); - } - } } public void addForegroundTimeLocked(long ttime) { @@ -6076,16 +6060,6 @@ public final class BatteryStatsImpl extends BatteryStats { } return val; } - - @Override - public long getTimeAtCpuSpeedStep(int speedStep, int which) { - if (speedStep < mSpeedBins.length) { - SamplingCounter c = mSpeedBins[speedStep]; - return c != null ? c.getCountLocked(which) : 0; - } else { - return 0; - } - } } /** @@ -6833,7 +6807,7 @@ public final class BatteryStatsImpl extends BatteryStats { final ByteArrayOutputStream memStream = new ByteArrayOutputStream(); try { XmlSerializer out = new FastXmlSerializer(); - out.setOutput(memStream, "utf-8"); + out.setOutput(memStream, StandardCharsets.UTF_8.name()); writeDailyItemsLocked(out); BackgroundThread.getHandler().post(new Runnable() { @Override @@ -6919,7 +6893,7 @@ public final class BatteryStatsImpl extends BatteryStats { } try { XmlPullParser parser = Xml.newPullParser(); - parser.setInput(stream, null); + parser.setInput(stream, StandardCharsets.UTF_8.name()); readDailyItemsLocked(parser); } catch (XmlPullParserException e) { } finally { @@ -7747,7 +7721,7 @@ public final class BatteryStatsImpl extends BatteryStats { * @param info The energy information from the bluetooth controller. */ public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info) { - if (info != null && mOnBatteryInternal && false) { + if (info != null && mOnBatteryInternal) { mHasBluetoothEnergyReporting = true; mBluetoothActivityCounters[CONTROLLER_RX_TIME].addCountLocked( info.getControllerRxTimeMillis()); @@ -7803,6 +7777,32 @@ public final class BatteryStatsImpl extends BatteryStats { } } + /** + * Read and distribute CPU usage across apps. + */ + public void updateCpuTimeLocked(boolean firstTime) { + final int cpuSpeedSteps = getCpuSpeedSteps(); + final long[] cpuSpeeds = mKernelCpuSpeedReader.readDelta(); + KernelUidCpuTimeReader.Callback callback = null; + if (mOnBatteryInternal && !firstTime) { + callback = new KernelUidCpuTimeReader.Callback() { + @Override + public void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs) { + final Uid u = getUidStatsLocked(mapUid(uid)); + u.mUserCpuTime.addCountLocked(userTimeUs); + u.mSystemCpuTime.addCountLocked(systemTimeUs); + for (int i = 0; i < cpuSpeedSteps; i++) { + if (u.mSpeedBins[i] == null) { + u.mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase); + } + u.mSpeedBins[i].addCountLocked(cpuSpeeds[i]); + } + } + }; + } + mKernelUidCpuTimeReader.readDelta(callback); + } + boolean setChargingLocked(boolean charging) { if (mCharging != charging) { mCharging = charging; @@ -7941,8 +7941,6 @@ public final class BatteryStatsImpl extends BatteryStats { boolean reset) { mRecordingHistory = true; mHistoryCur.currentTime = System.currentTimeMillis(); - mLastRecordedClockTime = mHistoryCur.currentTime; - mLastRecordedClockRealtime = elapsedRealtimeMs; addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME, mHistoryCur); @@ -7956,8 +7954,6 @@ public final class BatteryStatsImpl extends BatteryStats { final long uptimeMs) { if (mRecordingHistory) { mHistoryCur.currentTime = currentTime; - mLastRecordedClockTime = currentTime; - mLastRecordedClockRealtime = elapsedRealtimeMs; addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME, mHistoryCur); mHistoryCur.currentTime = 0; @@ -7967,8 +7963,6 @@ public final class BatteryStatsImpl extends BatteryStats { private void recordShutdownLocked(final long elapsedRealtimeMs, final long uptimeMs) { if (mRecordingHistory) { mHistoryCur.currentTime = System.currentTimeMillis(); - mLastRecordedClockTime = mHistoryCur.currentTime; - mLastRecordedClockRealtime = elapsedRealtimeMs; addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_SHUTDOWN, mHistoryCur); mHistoryCur.currentTime = 0; @@ -8431,6 +8425,7 @@ public final class BatteryStatsImpl extends BatteryStats { * Remove the statistics object for a particular uid. */ public void removeUidStatsLocked(int uid) { + mKernelUidCpuTimeReader.removeUid(uid); mUidStats.remove(uid); } @@ -8464,58 +8459,6 @@ public final class BatteryStatsImpl extends BatteryStats { return u.getServiceStatsLocked(pkg, name); } - /** - * Massage data to distribute any reasonable work down to more specific - * owners. Must only be called on a dead BatteryStats object! - */ - public void distributeWorkLocked(int which) { - // Aggregate all CPU time associated with WIFI. - Uid wifiUid = mUidStats.get(Process.WIFI_UID); - if (wifiUid != null) { - long uSecTime = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which); - for (int ip=wifiUid.mProcessStats.size()-1; ip>=0; ip--) { - Uid.Proc proc = wifiUid.mProcessStats.valueAt(ip); - long totalRunningTime = getGlobalWifiRunningTime(uSecTime, which); - for (int i=0; i<mUidStats.size(); i++) { - Uid uid = mUidStats.valueAt(i); - if (uid.mUid != Process.WIFI_UID) { - long uidRunningTime = uid.getWifiRunningTime(uSecTime, which); - if (uidRunningTime > 0) { - Uid.Proc uidProc = uid.getProcessStatsLocked("*wifi*"); - long time = proc.getUserTime(which); - time = (time*uidRunningTime)/totalRunningTime; - uidProc.mUserTime += time; - proc.mUserTime -= time; - time = proc.getSystemTime(which); - time = (time*uidRunningTime)/totalRunningTime; - uidProc.mSystemTime += time; - proc.mSystemTime -= time; - time = proc.getForegroundTime(which); - time = (time*uidRunningTime)/totalRunningTime; - uidProc.mForegroundTime += time; - proc.mForegroundTime -= time; - for (int sb=0; sb<proc.mSpeedBins.length; sb++) { - SamplingCounter sc = proc.mSpeedBins[sb]; - if (sc != null) { - time = sc.getCountLocked(which); - time = (time*uidRunningTime)/totalRunningTime; - SamplingCounter uidSc = uidProc.mSpeedBins[sb]; - if (uidSc == null) { - uidSc = new SamplingCounter(mOnBatteryTimeBase); - uidProc.mSpeedBins[sb] = uidSc; - } - uidSc.mCount.addAndGet((int)time); - sc.mCount.addAndGet((int)-time); - } - } - totalRunningTime -= uidRunningTime; - } - } - } - } - } - } - public void shutdownLocked() { recordShutdownLocked(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()); writeSyncLocked(); @@ -8974,6 +8917,23 @@ public final class BatteryStatsImpl extends BatteryStats { u.mMobileRadioActiveCount.readSummaryFromParcelLocked(in); } + u.mUserCpuTime.readSummaryFromParcelLocked(in); + u.mSystemCpuTime.readSummaryFromParcelLocked(in); + + int NSB = in.readInt(); + if (NSB > 100) { + Slog.w(TAG, "File corrupt: too many speed bins " + NSB); + return; + } + + u.mSpeedBins = new LongSamplingCounter[NSB]; + for (int i=0; i<NSB; i++) { + if (in.readInt() != 0) { + u.mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase); + u.mSpeedBins[i].readSummaryFromParcelLocked(in); + } + } + int NW = in.readInt(); if (NW > 100) { Slog.w(TAG, "File corrupt: too many wake locks " + NW); @@ -9031,18 +8991,6 @@ public final class BatteryStatsImpl extends BatteryStats { p.mStarts = p.mLoadedStarts = in.readInt(); p.mNumCrashes = p.mLoadedNumCrashes = in.readInt(); p.mNumAnrs = p.mLoadedNumAnrs = in.readInt(); - int NSB = in.readInt(); - if (NSB > 100) { - Slog.w(TAG, "File corrupt: too many speed bins " + NSB); - return; - } - p.mSpeedBins = new SamplingCounter[NSB]; - for (int i=0; i<NSB; i++) { - if (in.readInt() != 0) { - p.mSpeedBins[i] = new SamplingCounter(mOnBatteryTimeBase); - p.mSpeedBins[i].readSummaryFromParcelLocked(in); - } - } if (!p.readExcessivePowerFromParcelLocked(in)) { return; } @@ -9302,6 +9250,20 @@ public final class BatteryStatsImpl extends BatteryStats { u.mMobileRadioActiveCount.writeSummaryFromParcelLocked(out); } + u.mUserCpuTime.writeSummaryFromParcelLocked(out); + u.mSystemCpuTime.writeSummaryFromParcelLocked(out); + + out.writeInt(u.mSpeedBins.length); + for (int i = 0; i < u.mSpeedBins.length; i++) { + LongSamplingCounter speedBin = u.mSpeedBins[i]; + if (speedBin != null) { + out.writeInt(1); + speedBin.writeSummaryFromParcelLocked(out); + } else { + out.writeInt(0); + } + } + final ArrayMap<String, Uid.Wakelock> wakeStats = u.mWakelockStats.getMap(); int NW = wakeStats.size(); out.writeInt(NW); @@ -9368,16 +9330,6 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(ps.mStarts); out.writeInt(ps.mNumCrashes); out.writeInt(ps.mNumAnrs); - final int N = ps.mSpeedBins.length; - out.writeInt(N); - for (int i=0; i<N; i++) { - if (ps.mSpeedBins[i] != null) { - out.writeInt(1); - ps.mSpeedBins[i].writeSummaryFromParcelLocked(out); - } else { - out.writeInt(0); - } - } ps.writeExcessivePowerToParcelLocked(out); } diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java index 6c3f958..a3ef612 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -44,55 +44,57 @@ public class CpuPowerCalculator extends PowerCalculator { long rawUptimeUs, int statsType) { final int speedSteps = mSpeedStepTimes.length; + long totalTimeAtSpeeds = 0; + for (int step = 0; step < speedSteps; step++) { + mSpeedStepTimes[step] = u.getTimeAtCpuSpeed(step, statsType); + totalTimeAtSpeeds += mSpeedStepTimes[step]; + } + totalTimeAtSpeeds = Math.max(totalTimeAtSpeeds, 1); + + app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000; + if (DEBUG && app.cpuTimeMs != 0) { + Log.d(TAG, "UID " + u.getUid() + ": CPU time " + app.cpuTimeMs + " ms"); + } + + double cpuPowerMaMs = 0; + for (int step = 0; step < speedSteps; step++) { + final double ratio = (double) mSpeedStepTimes[step] / totalTimeAtSpeeds; + final double cpuSpeedStepPower = ratio * app.cpuTimeMs * mPowerCpuNormal[step]; + if (DEBUG && ratio != 0) { + Log.d(TAG, "UID " + u.getUid() + ": CPU step #" + + step + " ratio=" + BatteryStatsHelper.makemAh(ratio) + " power=" + + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000))); + } + cpuPowerMaMs += cpuSpeedStepPower; + } + + if (DEBUG && cpuPowerMaMs != 0) { + Log.d(TAG, "UID " + u.getUid() + ": cpu total power=" + + BatteryStatsHelper.makemAh(cpuPowerMaMs / (60 * 60 * 1000))); + } + // Keep track of the package with highest drain. double highestDrain = 0; + app.cpuFgTimeMs = 0; final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); final int processStatsCount = processStats.size(); for (int i = 0; i < processStatsCount; i++) { final BatteryStats.Uid.Proc ps = processStats.valueAt(i); final String processName = processStats.keyAt(i); - app.cpuFgTimeMs += ps.getForegroundTime(statsType); - final long totalCpuTime = ps.getUserTime(statsType) + ps.getSystemTime(statsType); - app.cpuTimeMs += totalCpuTime; - - // Calculate the total CPU time spent at the various speed steps. - long totalTimeAtSpeeds = 0; - for (int step = 0; step < speedSteps; step++) { - mSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, statsType); - totalTimeAtSpeeds += mSpeedStepTimes[step]; - } - totalTimeAtSpeeds = Math.max(totalTimeAtSpeeds, 1); - - // Then compute the ratio of time spent at each speed and figure out - // the total power consumption. - double cpuPower = 0; - for (int step = 0; step < speedSteps; step++) { - final double ratio = (double) mSpeedStepTimes[step] / totalTimeAtSpeeds; - final double cpuSpeedStepPower = ratio * totalCpuTime * mPowerCpuNormal[step]; - if (DEBUG && ratio != 0) { - Log.d(TAG, "UID " + u.getUid() + ": CPU step #" - + step + " ratio=" + BatteryStatsHelper.makemAh(ratio) + " power=" - + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000))); - } - cpuPower += cpuSpeedStepPower; - } - if (DEBUG && cpuPower != 0) { - Log.d(TAG, String.format("process %s, cpu power=%s", - processName, BatteryStatsHelper.makemAh(cpuPower / (60 * 60 * 1000)))); - } - app.cpuPowerMah += cpuPower; + final long costValue = ps.getUserTime(statsType) + ps.getSystemTime(statsType) + + ps.getForegroundTime(statsType); // Each App can have multiple packages and with multiple running processes. // Keep track of the package who's process has the highest drain. if (app.packageWithHighestDrain == null || app.packageWithHighestDrain.startsWith("*")) { - highestDrain = cpuPower; + highestDrain = costValue; app.packageWithHighestDrain = processName; - } else if (highestDrain < cpuPower && !processName.startsWith("*")) { - highestDrain = cpuPower; + } else if (highestDrain < costValue && !processName.startsWith("*")) { + highestDrain = costValue; app.packageWithHighestDrain = processName; } } @@ -108,6 +110,6 @@ public class CpuPowerCalculator extends PowerCalculator { } // Convert the CPU power to mAh - app.cpuPowerMah /= (60 * 60 * 1000); + app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000); } } diff --git a/core/java/com/android/internal/os/KernelCpuSpeedReader.java b/core/java/com/android/internal/os/KernelCpuSpeedReader.java new file mode 100644 index 0000000..c30df28 --- /dev/null +++ b/core/java/com/android/internal/os/KernelCpuSpeedReader.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.os; + +import android.text.TextUtils; +import android.util.Slog; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.Arrays; + +/** + * Reads CPU time spent at various frequencies and provides a delta from the last call to + * {@link #readDelta}. Each line in the proc file has the format: + * + * freq time + * + * where time is measured in 1/100 seconds. + */ +public class KernelCpuSpeedReader { + private static final String TAG = "KernelCpuSpeedReader"; + private static final String sProcFile = + "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state"; + private static final int MAX_SPEEDS = 60; + + private long[] mLastSpeedTimes = new long[MAX_SPEEDS]; + private long[] mDeltaSpeedTimes = new long[MAX_SPEEDS]; + + /** + * The returned array is modified in subsequent calls to {@link #readDelta}. + * @return The time (in milliseconds) spent at different cpu speeds since the last call to + * {@link #readDelta}. + */ + public long[] readDelta() { + try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) { + TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' '); + String line; + int speedIndex = 0; + while ((line = reader.readLine()) != null) { + splitter.setString(line); + Long.parseLong(splitter.next()); + + // The proc file reports time in 1/100 sec, so convert to milliseconds. + long time = Long.parseLong(splitter.next()) * 10; + mDeltaSpeedTimes[speedIndex] = time - mLastSpeedTimes[speedIndex]; + mLastSpeedTimes[speedIndex] = time; + speedIndex++; + } + } catch (IOException e) { + Slog.e(TAG, "Failed to read cpu-freq", e); + Arrays.fill(mDeltaSpeedTimes, 0); + } + return mDeltaSpeedTimes; + } +} diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java new file mode 100644 index 0000000..b236378 --- /dev/null +++ b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.os; + +import android.annotation.Nullable; +import android.text.TextUtils; +import android.util.Slog; +import android.util.SparseLongArray; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +/** + * Reads /proc/uid_cputime/show_uid_stat which has the line format: + * + * uid: user_time_micro_seconds system_time_micro_seconds + * + * This provides the time a UID's processes spent executing in user-space and kernel-space. + * The file contains a monotonically increasing count of time for a single boot. This class + * maintains the previous results of a call to {@link #readDelta} in order to provide a proper + * delta. + */ +public class KernelUidCpuTimeReader { + private static final String TAG = "KernelUidCpuTimeReader"; + private static final String sProcFile = "/proc/uid_cputime/show_uid_stat"; + private static final String sRemoveUidProcFile = "/proc/uid_cputime/remove_uid_range"; + + /** + * Callback interface for processing each line of the proc file. + */ + public interface Callback { + void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs); + } + + private SparseLongArray mLastUserTimeUs = new SparseLongArray(); + private SparseLongArray mLastSystemTimeUs = new SparseLongArray(); + + /** + * Reads the proc file, calling into the callback with a delta of time for each UID. + * @param callback The callback to invoke for each line of the proc file. If null, + * the data is consumed and subsequent calls to readDelta will provide + * a fresh delta. + */ + public void readDelta(@Nullable Callback callback) { + try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) { + TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' '); + String line; + while ((line = reader.readLine()) != null) { + splitter.setString(line); + final String uidStr = splitter.next(); + final int uid = Integer.parseInt(uidStr.substring(0, uidStr.length() - 1), 10); + final long userTimeUs = Long.parseLong(splitter.next(), 10); + final long systemTimeUs = Long.parseLong(splitter.next(), 10); + + if (callback != null) { + long userTimeDeltaUs = userTimeUs; + long systemTimeDeltaUs = systemTimeUs; + int index = mLastUserTimeUs.indexOfKey(uid); + if (index >= 0) { + userTimeDeltaUs -= mLastUserTimeUs.valueAt(index); + systemTimeDeltaUs -= mLastSystemTimeUs.valueAt(index); + + if (userTimeDeltaUs < 0 || systemTimeDeltaUs < 0) { + // The UID must have been removed from accounting, then added back. + userTimeDeltaUs = userTimeUs; + systemTimeDeltaUs = systemTimeUs; + } + } + + if (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0) { + callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs); + } + } + mLastUserTimeUs.put(uid, userTimeUs); + mLastSystemTimeUs.put(uid, systemTimeUs); + } + } catch (IOException e) { + Slog.e(TAG, "Failed to read uid_cputime", e); + } + } + + /** + * Removes the UID from the kernel module and from internal accounting data. + * @param uid The UID to remove. + */ + public void removeUid(int uid) { + int index = mLastUserTimeUs.indexOfKey(uid); + if (index >= 0) { + mLastUserTimeUs.removeAt(index); + mLastSystemTimeUs.removeAt(index); + } + + try (FileWriter writer = new FileWriter(sRemoveUidProcFile)) { + writer.write(Integer.toString(uid) + "-" + Integer.toString(uid)); + writer.flush(); + } catch (IOException e) { + Slog.e(TAG, "failed to remove uid from uid_cputime module", e); + } + } +} diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java index 2983047..8393e2a 100644 --- a/core/java/com/android/internal/os/ProcessCpuTracker.java +++ b/core/java/com/android/internal/os/ProcessCpuTracker.java @@ -170,21 +170,6 @@ public class ProcessCpuTracker { private byte[] mBuffer = new byte[4096]; - /** - * The time in microseconds that the CPU has been running at each speed. - */ - private long[] mCpuSpeedTimes; - - /** - * The relative time in microseconds that the CPU has been running at each speed. - */ - private long[] mRelCpuSpeedTimes; - - /** - * The different speeds that the CPU can be running at. - */ - private long[] mCpuSpeeds; - public static class Stats { public final int pid; public final int uid; @@ -590,70 +575,6 @@ public class ProcessCpuTracker { } } - /** - * Returns the delta time (in clock ticks, or 1/100 sec) spent at each CPU - * speed, since the last call to this method. If this is the first call, it - * will return 1 for each value. - */ - public long[] getLastCpuSpeedTimes() { - if (mCpuSpeedTimes == null) { - mCpuSpeedTimes = getCpuSpeedTimes(null); - mRelCpuSpeedTimes = new long[mCpuSpeedTimes.length]; - for (int i = 0; i < mCpuSpeedTimes.length; i++) { - mRelCpuSpeedTimes[i] = 1; // Initialize - } - } else { - getCpuSpeedTimes(mRelCpuSpeedTimes); - for (int i = 0; i < mCpuSpeedTimes.length; i++) { - long temp = mRelCpuSpeedTimes[i]; - mRelCpuSpeedTimes[i] -= mCpuSpeedTimes[i]; - mCpuSpeedTimes[i] = temp; - } - } - return mRelCpuSpeedTimes; - } - - private long[] getCpuSpeedTimes(long[] out) { - long[] tempTimes = out; - long[] tempSpeeds = mCpuSpeeds; - final int MAX_SPEEDS = 60; - if (out == null) { - tempTimes = new long[MAX_SPEEDS]; // Hopefully no more than that - tempSpeeds = new long[MAX_SPEEDS]; - } - int speed = 0; - String file = readFile("/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state", '\0'); - // Note: file may be null on kernels without cpufreq (i.e. the emulator's) - if (file != null) { - StringTokenizer st = new StringTokenizer(file, "\n "); - while (st.hasMoreElements()) { - String token = st.nextToken(); - try { - long val = Long.parseLong(token); - tempSpeeds[speed] = val; - token = st.nextToken(); - val = Long.parseLong(token); - tempTimes[speed] = val; - speed++; - if (speed == MAX_SPEEDS) break; // No more - if (localLOGV && out == null) { - Slog.v(TAG, "First time : Speed/Time = " + tempSpeeds[speed - 1] - + "\t" + tempTimes[speed - 1]); - } - } catch (NumberFormatException nfe) { - Slog.i(TAG, "Unable to parse time_in_state"); - } - } - } - if (out == null) { - out = new long[speed]; - mCpuSpeeds = new long[speed]; - System.arraycopy(tempSpeeds, 0, mCpuSpeeds, 0, speed); - System.arraycopy(tempTimes, 0, out, 0, speed); - } - return out; - } - final public int getLastUserTime() { return mRelUserTime; } diff --git a/core/java/com/android/internal/policy/IFaceLockInterface.aidl b/core/java/com/android/internal/policy/IFaceLockInterface.aidl deleted file mode 100644 index bc1f002..0000000 --- a/core/java/com/android/internal/policy/IFaceLockInterface.aidl +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.internal.policy; - -import android.os.IBinder; -import com.android.internal.policy.IFaceLockCallback; - -/** {@hide} */ -interface IFaceLockInterface { - void startUi(IBinder containingWindowToken, int x, int y, int width, int height, - boolean useLiveliness); - void stopUi(); - void startWithoutUi(); - void registerCallback(IFaceLockCallback cb); - void unregisterCallback(IFaceLockCallback cb); -} diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index a578a6e..bc64373 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -3376,9 +3376,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mFloatingActionMode.finish(); } cleanupFloatingActionModeViews(); - mFloatingToolbar = new FloatingToolbar(mContext, PhoneWindow.this); - final FloatingActionMode mode = new FloatingActionMode( - mContext, callback, originatingView, mFloatingToolbar); + final FloatingActionMode mode = + new FloatingActionMode(mContext, callback, originatingView); mFloatingActionModeOriginatingView = originatingView; mFloatingToolbarPreDrawListener = new OnPreDrawListener() { @@ -3393,6 +3392,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private void setHandledFloatingActionMode(ActionMode mode) { mFloatingActionMode = mode; + mFloatingToolbar = new FloatingToolbar(mContext, PhoneWindow.this); + ((FloatingActionMode) mFloatingActionMode).setFloatingToolbar(mFloatingToolbar); mFloatingActionMode.invalidate(); mFloatingToolbar.show(); mFloatingActionModeOriginatingView.getViewTreeObserver() diff --git a/core/java/com/android/internal/statusbar/StatusBarIcon.java b/core/java/com/android/internal/statusbar/StatusBarIcon.java index e0792cb..4693d4b 100644 --- a/core/java/com/android/internal/statusbar/StatusBarIcon.java +++ b/core/java/com/android/internal/statusbar/StatusBarIcon.java @@ -16,40 +16,46 @@ package com.android.internal.statusbar; +import android.graphics.drawable.Icon; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; public class StatusBarIcon implements Parcelable { - public String iconPackage; public UserHandle user; - public int iconId; + public Icon icon; public int iconLevel; public boolean visible = true; public int number; public CharSequence contentDescription; - public StatusBarIcon(String iconPackage, UserHandle user, int iconId, int iconLevel, int number, + public StatusBarIcon(UserHandle user, Icon icon, int iconLevel, int number, CharSequence contentDescription) { - this.iconPackage = iconPackage; this.user = user; - this.iconId = iconId; + this.icon = icon; this.iconLevel = iconLevel; this.number = number; this.contentDescription = contentDescription; } + public StatusBarIcon(String iconPackage, UserHandle user, + int iconId, int iconLevel, int number, + CharSequence contentDescription) { + this(user, Icon.createWithResource(iconPackage, iconId), + iconLevel, number, contentDescription); + } + @Override public String toString() { - return "StatusBarIcon(pkg=" + this.iconPackage + "user=" + user.getIdentifier() - + " id=0x" + Integer.toHexString(this.iconId) + return "StatusBarIcon(icon=" + this.icon + + " user=" + user.getIdentifier() + " level=" + this.iconLevel + " visible=" + visible + " num=" + this.number + " )"; } @Override public StatusBarIcon clone() { - StatusBarIcon that = new StatusBarIcon(this.iconPackage, this.user, this.iconId, + StatusBarIcon that = new StatusBarIcon(this.user, this.icon, this.iconLevel, this.number, this.contentDescription); that.visible = this.visible; return that; @@ -63,9 +69,8 @@ public class StatusBarIcon implements Parcelable { } public void readFromParcel(Parcel in) { - this.iconPackage = in.readString(); + this.icon = (Icon) in.readParcelable(null); this.user = (UserHandle) in.readParcelable(null); - this.iconId = in.readInt(); this.iconLevel = in.readInt(); this.visible = in.readInt() != 0; this.number = in.readInt(); @@ -73,9 +78,8 @@ public class StatusBarIcon implements Parcelable { } public void writeToParcel(Parcel out, int flags) { - out.writeString(this.iconPackage); + out.writeParcelable(this.icon, 0); out.writeParcelable(this.user, 0); - out.writeInt(this.iconId); out.writeInt(this.iconLevel); out.writeInt(this.visible ? 1 : 0); out.writeInt(this.number); diff --git a/core/java/com/android/internal/util/ImageUtils.java b/core/java/com/android/internal/util/ImageUtils.java index a0d0b20..7d56e9e 100644 --- a/core/java/com/android/internal/util/ImageUtils.java +++ b/core/java/com/android/internal/util/ImageUtils.java @@ -66,7 +66,7 @@ public class ImageUtils { COMPACT_BITMAP_SIZE, COMPACT_BITMAP_SIZE, Bitmap.Config.ARGB_8888 ); mTempCompactBitmapCanvas = new Canvas(mTempCompactBitmap); - mTempCompactBitmapPaint = new Paint(); + mTempCompactBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTempCompactBitmapPaint.setFilterBitmap(true); } mTempMatrix.reset(); diff --git a/core/java/com/android/internal/util/NotificationColorUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java index 3249ea3..6076973 100644 --- a/core/java/com/android/internal/util/NotificationColorUtil.java +++ b/core/java/com/android/internal/util/NotificationColorUtil.java @@ -24,6 +24,7 @@ import android.graphics.Color; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.graphics.drawable.VectorDrawable; import android.text.SpannableStringBuilder; import android.text.Spanned; @@ -129,6 +130,20 @@ public class NotificationColorUtil { } } + public boolean isGrayscaleIcon(Context context, Icon icon) { + if (icon == null) { + return false; + } + switch (icon.getType()) { + case Icon.TYPE_BITMAP: + return isGrayscaleIcon(icon.getBitmap()); + case Icon.TYPE_RESOURCE: + return isGrayscaleIcon(context, icon.getResId()); + default: + return false; + } + } + /** * Checks whether a drawable with a resoure id is a small grayscale icon. * Grayscale here means "very close to a perfect gray"; icon means "no larger than 64dp". diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java index 0350d61..32746c2 100644 --- a/core/java/com/android/internal/util/XmlUtils.java +++ b/core/java/com/android/internal/util/XmlUtils.java @@ -33,6 +33,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ProtocolException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -182,7 +183,7 @@ public class XmlUtils { public static final void writeMapXml(Map val, OutputStream out) throws XmlPullParserException, java.io.IOException { XmlSerializer serializer = new FastXmlSerializer(); - serializer.setOutput(out, "utf-8"); + serializer.setOutput(out, StandardCharsets.UTF_8.name()); serializer.startDocument(null, true); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); writeMapXml(val, null, serializer); @@ -205,7 +206,7 @@ public class XmlUtils { throws XmlPullParserException, java.io.IOException { XmlSerializer serializer = Xml.newSerializer(); - serializer.setOutput(out, "utf-8"); + serializer.setOutput(out, StandardCharsets.UTF_8.name()); serializer.startDocument(null, true); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); writeListXml(val, null, serializer); @@ -732,7 +733,7 @@ public class XmlUtils { throws XmlPullParserException, java.io.IOException { XmlPullParser parser = Xml.newPullParser(); - parser.setInput(in, null); + parser.setInput(in, StandardCharsets.UTF_8.name()); return (HashMap<String, ?>) readValueXml(parser, new String[1]); } @@ -753,7 +754,7 @@ public class XmlUtils { throws XmlPullParserException, java.io.IOException { XmlPullParser parser = Xml.newPullParser(); - parser.setInput(in, null); + parser.setInput(in, StandardCharsets.UTF_8.name()); return (ArrayList)readValueXml(parser, new String[1]); } diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index 993ab58..e27ba13 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -100,7 +100,11 @@ public class BaseIWindow extends IWindow.Stub { } @Override - public void doneAnimating() { + public void onAnimationStarted(int remainingFrameCount) { + } + + @Override + public void onAnimationStopped() { } @Override diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java index aacdb34..c3f4da7 100644 --- a/core/java/com/android/internal/view/FloatingActionMode.java +++ b/core/java/com/android/internal/view/FloatingActionMode.java @@ -24,6 +24,7 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import com.android.internal.util.Preconditions; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.widget.FloatingToolbar; @@ -32,20 +33,28 @@ public class FloatingActionMode extends ActionMode { private final Context mContext; private final ActionMode.Callback2 mCallback; private final MenuBuilder mMenu; - private final FloatingToolbar mFloatingToolbar; private final Rect mContentRect; private final Rect mContentRectOnWindow; private final Rect mPreviousContentRectOnWindow; private final int[] mViewPosition; private final View mOriginatingView; + private FloatingToolbar mFloatingToolbar; public FloatingActionMode( - Context context, ActionMode.Callback2 callback, View originatingView, - FloatingToolbar floatingToolbar) { + Context context, ActionMode.Callback2 callback, View originatingView) { mContext = context; mCallback = callback; mMenu = new MenuBuilder(context).setDefaultShowAsAction( MenuItem.SHOW_AS_ACTION_IF_ROOM); + setType(ActionMode.TYPE_FLOATING); + mContentRect = new Rect(); + mContentRectOnWindow = new Rect(); + mPreviousContentRectOnWindow = new Rect(); + mViewPosition = new int[2]; + mOriginatingView = originatingView; + } + + public void setFloatingToolbar(FloatingToolbar floatingToolbar) { mFloatingToolbar = floatingToolbar .setMenu(mMenu) .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { @@ -54,12 +63,6 @@ public class FloatingActionMode extends ActionMode { return mCallback.onActionItemClicked(FloatingActionMode.this, item); } }); - setType(ActionMode.TYPE_FLOATING); - mContentRect = new Rect(); - mContentRectOnWindow = new Rect(); - mPreviousContentRectOnWindow = new Rect(); - mViewPosition = new int[2]; - mOriginatingView = originatingView; } @Override @@ -79,6 +82,7 @@ public class FloatingActionMode extends ActionMode { @Override public void invalidate() { + Preconditions.checkNotNull(mFloatingToolbar); mCallback.onPrepareActionMode(this, mMenu); mFloatingToolbar.updateLayout(); invalidateContentRect(); @@ -86,11 +90,13 @@ public class FloatingActionMode extends ActionMode { @Override public void invalidateContentRect() { + Preconditions.checkNotNull(mFloatingToolbar); mCallback.onGetContentRect(this, mOriginatingView, mContentRect); repositionToolbar(); } public void updateViewLocationInWindow() { + Preconditions.checkNotNull(mFloatingToolbar); mOriginatingView.getLocationInWindow(mViewPosition); repositionToolbar(); } diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java index f98fbfc..fdc3547 100644 --- a/core/java/com/android/internal/widget/FloatingToolbar.java +++ b/core/java/com/android/internal/widget/FloatingToolbar.java @@ -79,7 +79,6 @@ public final class FloatingToolbar { private final FloatingToolbarPopup mPopup; private final Rect mContentRect = new Rect(); - private final Point mCoordinates = new Point(); private Menu mMenu; private List<CharSequence> mShowingTitles = new ArrayList<CharSequence>(); @@ -87,7 +86,6 @@ public final class FloatingToolbar { private int mSuggestedWidth; private boolean mWidthChanged = true; - private int mOverflowDirection; /** * Initializes a floating toolbar. @@ -157,11 +155,9 @@ public final class FloatingToolbar { mPopup.layoutMenuItems(menuItems, mMenuItemClickListener, mSuggestedWidth); mShowingTitles = getMenuItemTitles(menuItems); } - refreshCoordinates(); - mPopup.setOverflowDirection(mOverflowDirection); - mPopup.updateCoordinates(mCoordinates.x, mCoordinates.y); + mPopup.updateCoordinates(mContentRect); if (!mPopup.isShowing()) { - mPopup.show(mCoordinates.x, mCoordinates.y); + mPopup.show(mContentRect); } mWidthChanged = false; return this; @@ -209,25 +205,6 @@ public final class FloatingToolbar { } /** - * Refreshes {@link #mCoordinates} with values based on {@link #mContentRect}. - */ - private void refreshCoordinates() { - int x = mContentRect.centerX() - mPopup.getWidth() / 2; - int y; - if (mContentRect.top > mPopup.getHeight()) { - y = mContentRect.top - mPopup.getHeight(); - mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_UP; - } else if (mContentRect.top > mPopup.getToolbarHeightWithVerticalMargin()) { - y = mContentRect.top - mPopup.getToolbarHeightWithVerticalMargin(); - mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN; - } else { - y = mContentRect.bottom; - mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN; - } - mCoordinates.set(x, y); - } - - /** * Returns true if this floating toolbar is currently showing the specified menu items. */ private boolean isCurrentlyShowing(List<MenuItem> menuItems) { @@ -345,6 +322,8 @@ public final class FloatingToolbar { } }; + private final Point mCoords = new Point(); + private final Region mTouchableRegion = new Region(); private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = new ViewTreeObserver.OnComputeInternalInsetsListener() { @@ -378,6 +357,7 @@ public final class FloatingToolbar { mShowAnimation = createGrowFadeInFromBottom(mContentContainer); mDismissAnimation = createShrinkFadeOutFromBottomAnimation( mContentContainer, + 0, new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -387,6 +367,7 @@ public final class FloatingToolbar { }); mHideAnimation = createShrinkFadeOutFromBottomAnimation( mContentContainer, + 150, new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -404,6 +385,8 @@ public final class FloatingToolbar { */ public void layoutMenuItems(List<MenuItem> menuItems, MenuItem.OnMenuItemClickListener menuItemClickListener, int suggestedWidth) { + Preconditions.checkNotNull(menuItems); + mContentContainer.removeAllViews(); if (mMainPanel == null) { mMainPanel = new FloatingToolbarMainPanel(mParent.getContext(), mOpenOverflow); @@ -426,7 +409,9 @@ public final class FloatingToolbar { * Shows this popup at the specified coordinates. * The specified coordinates may be adjusted to make sure the popup is entirely on-screen. */ - public void show(int x, int y) { + public void show(Rect contentRect) { + Preconditions.checkNotNull(contentRect); + if (isShowing()) { return; } @@ -435,6 +420,7 @@ public final class FloatingToolbar { mDismissed = false; cancelDismissAndHideAnimations(); cancelOverflowAnimations(); + // Make sure a panel is set as the content. if (mContentContainer.getChildCount() == 0) { setMainPanelAsContent(); @@ -442,8 +428,10 @@ public final class FloatingToolbar { // The "show" animation will make this visible. mContentContainer.setAlpha(0); } + updateOverflowHeight(contentRect.top - (mMarginVertical * 2)); + refreshCoordinatesAndOverflowDirection(contentRect); preparePopupContent(); - mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, x, y); + mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, mCoords.x, mCoords.y); setTouchableSurfaceInsetsComputer(); runShowAnimation(); } @@ -496,27 +484,17 @@ public final class FloatingToolbar { * The specified coordinates may be adjusted to make sure the popup is entirely on-screen. * This is a no-op if this popup is not showing. */ - public void updateCoordinates(int x, int y) { + public void updateCoordinates(Rect contentRect) { + Preconditions.checkNotNull(contentRect); + if (!isShowing() || !mPopupWindow.isShowing()) { return; } cancelOverflowAnimations(); + refreshCoordinatesAndOverflowDirection(contentRect); preparePopupContent(); - mPopupWindow.update(x, y, getWidth(), getHeight()); - } - - /** - * Sets the direction in which the overflow will open. i.e. up or down. - * - * @param overflowDirection Either {@link #OVERFLOW_DIRECTION_UP} - * or {@link #OVERFLOW_DIRECTION_DOWN}. - */ - public void setOverflowDirection(int overflowDirection) { - mOverflowDirection = overflowDirection; - if (mOverflowPanel != null) { - mOverflowPanel.setOverflowDirection(mOverflowDirection); - } + mPopupWindow.update(mCoords.x, mCoords.y, getWidth(), getHeight()); } /** @@ -540,7 +518,26 @@ public final class FloatingToolbar { return mContentContainer.getContext(); } - int getToolbarHeightWithVerticalMargin() { + private void refreshCoordinatesAndOverflowDirection(Rect contentRect) { + int x = contentRect.centerX() - getWidth() / 2; + int y; + if (contentRect.top > getHeight()) { + y = contentRect.top - getHeight(); + mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_UP; + } else if (contentRect.top > getToolbarHeightWithVerticalMargin()) { + y = contentRect.top - getToolbarHeightWithVerticalMargin(); + mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN; + } else { + y = contentRect.bottom; + mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN; + } + mCoords.set(x, y); + if (mOverflowPanel != null) { + mOverflowPanel.setOverflowDirection(mOverflowDirection); + } + } + + private int getToolbarHeightWithVerticalMargin() { return getEstimatedToolbarHeight(mParent.getContext()) + mMarginVertical * 2; } @@ -693,16 +690,24 @@ public final class FloatingToolbar { } // Reset position. - if (mMainPanel != null - && mContentContainer.getChildAt(0) == mMainPanel.getView()) { + if (isMainPanelContent()) { positionMainPanel(); } - if (mOverflowPanel != null - && mContentContainer.getChildAt(0) == mOverflowPanel.getView()) { + if (isOverflowPanelContent()) { positionOverflowPanel(); } } + private boolean isMainPanelContent() { + return mMainPanel != null + && mContentContainer.getChildAt(0) == mMainPanel.getView(); + } + + private boolean isOverflowPanelContent() { + return mOverflowPanel != null + && mContentContainer.getChildAt(0) == mOverflowPanel.getView(); + } + /** * Sets the current content to be the main view panel. */ @@ -765,6 +770,25 @@ public final class FloatingToolbar { setContentAreaAsTouchableSurface(); } + private void updateOverflowHeight(int height) { + if (mOverflowPanel != null) { + mOverflowPanel.setSuggestedHeight(height); + + // Re-measure the popup and it's contents. + boolean mainPanelContent = isMainPanelContent(); + boolean overflowPanelContent = isOverflowPanelContent(); + mContentContainer.removeAllViews(); // required to update popup size. + updatePopupSize(); + // Reset the appropriate content. + if (mainPanelContent) { + setMainPanelAsContent(); + } + if (overflowPanelContent) { + setOverflowPanelAsContent(); + } + } + } + private void updatePopupSize() { int width = 0; int height = 0; @@ -864,6 +888,8 @@ public final class FloatingToolbar { * @return The menu items that are not included in this main panel. */ public List<MenuItem> layoutMenuItems(List<MenuItem> menuItems, int suggestedWidth) { + Preconditions.checkNotNull(menuItems); + final int toolbarWidth = getAdjustedToolbarWidth(mContext, suggestedWidth) // Reserve space for the "open overflow" button. - getEstimatedOpenOverflowButtonWidth(mContext); @@ -972,6 +998,7 @@ public final class FloatingToolbar { private MenuItem.OnMenuItemClickListener mOnMenuItemClickListener; private int mOverflowWidth = 0; + private int mSuggestedHeight; /** * Initializes a floating toolbar popup overflow view panel. @@ -981,6 +1008,7 @@ public final class FloatingToolbar { */ public FloatingToolbarOverflowPanel(Context context, Runnable closeOverflow) { mCloseOverflow = Preconditions.checkNotNull(closeOverflow); + mSuggestedHeight = getScreenHeight(context); mContentView = new LinearLayout(context); mContentView.setOrientation(LinearLayout.VERTICAL); @@ -1043,6 +1071,11 @@ public final class FloatingToolbar { mContentView.addView(mBackButtonContainer, index); } + public void setSuggestedHeight(int height) { + mSuggestedHeight = height; + setListViewHeight(); + } + /** * Returns the content view of the overflow. */ @@ -1074,9 +1107,17 @@ public final class FloatingToolbar { int itemHeight = getEstimatedToolbarHeight(mContentView.getContext()); int height = mListView.getAdapter().getCount() * itemHeight; int maxHeight = mContentView.getContext().getResources(). + getDimensionPixelSize(R.dimen.floating_toolbar_maximum_overflow_height); + int minHeight = mContentView.getContext().getResources(). getDimensionPixelSize(R.dimen.floating_toolbar_minimum_overflow_height); + int availableHeight = mSuggestedHeight - (mSuggestedHeight % itemHeight) + - itemHeight; // reserve space for the back button. ViewGroup.LayoutParams params = mListView.getLayoutParams(); - params.height = Math.min(height, maxHeight); + if (availableHeight >= minHeight) { + params.height = Math.min(Math.min(availableHeight, maxHeight), height); + } else { + params.height = Math.min(maxHeight, height); + } mListView.setLayoutParams(params); } @@ -1224,15 +1265,16 @@ public final class FloatingToolbar { * Creates a "shrink and fade out from bottom" animation for the specified view. * * @param view The view to animate + * @param startDelay The start delay of the animation * @param listener The animation listener */ private static AnimatorSet createShrinkFadeOutFromBottomAnimation( - View view, Animator.AnimatorListener listener) { + View view, int startDelay, Animator.AnimatorListener listener) { AnimatorSet shrinkFadeOutFromBottomAnimation = new AnimatorSet(); shrinkFadeOutFromBottomAnimation.playTogether( ObjectAnimator.ofFloat(view, View.SCALE_Y, 1, 0.5f).setDuration(125), ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(75)); - shrinkFadeOutFromBottomAnimation.setStartDelay(150); + shrinkFadeOutFromBottomAnimation.setStartDelay(startDelay); shrinkFadeOutFromBottomAnimation.addListener(listener); return shrinkFadeOutFromBottomAnimation; } @@ -1271,16 +1313,4 @@ public final class FloatingToolbar { private static int getScreenHeight(Context context) { return context.getResources().getDisplayMetrics().heightPixels; } - - /** - * Returns value, restricted to the range min->max (inclusive). - * If maximum is less than minimum, the result is undefined. - * - * @param value The value to clamp. - * @param minimum The minimum value in the range. - * @param maximum The maximum value in the range. Must not be less than minimum. - */ - private static int clamp(int value, int minimum, int maximum) { - return Math.max(minimum, Math.min(value, maximum)); - } } diff --git a/core/java/com/android/internal/widget/LockPatternChecker.java b/core/java/com/android/internal/widget/LockPatternChecker.java new file mode 100644 index 0000000..ac0f5fe --- /dev/null +++ b/core/java/com/android/internal/widget/LockPatternChecker.java @@ -0,0 +1,146 @@ +package com.android.internal.widget; + +import android.os.AsyncTask; + +import java.util.List; + +/** + * Helper class to check/verify PIN/Password/Pattern asynchronously. + */ +public final class LockPatternChecker { + /** + * Interface for a callback to be invoked after security check. + */ + public interface OnCheckCallback { + /** + * Invoked when a security check is finished. + * + * @param matched Whether the PIN/Password/Pattern matches the stored one. + */ + void onChecked(boolean matched); + } + + /** + * Interface for a callback to be invoked after security verification. + */ + public interface OnVerifyCallback { + /** + * Invoked when a security verification is finished. + * + * @param attestation The attestation that the challenge was verified, or null. + */ + void onVerified(byte[] attestation); + } + + /** + * Verify a pattern asynchronously. + * + * @param utils The LockPatternUtils instance to use. + * @param pattern The pattern to check. + * @param challenge The challenge to verify against the pattern. + * @param userId The user to check against the pattern. + * @param callback The callback to be invoked with the verification result. + */ + public static AsyncTask<?, ?, ?> verifyPattern(final LockPatternUtils utils, + final List<LockPatternView.Cell> pattern, + final long challenge, + final int userId, + final OnVerifyCallback callback) { + AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() { + @Override + protected byte[] doInBackground(Void... args) { + return utils.verifyPattern(pattern, challenge, userId); + } + + @Override + protected void onPostExecute(byte[] result) { + callback.onVerified(result); + } + }; + task.execute(); + return task; + } + + /** + * Checks a pattern asynchronously. + * + * @param utils The LockPatternUtils instance to use. + * @param pattern The pattern to check. + * @param userId The user to check against the pattern. + * @param callback The callback to be invoked with the check result. + */ + public static AsyncTask<?, ?, ?> checkPattern(final LockPatternUtils utils, + final List<LockPatternView.Cell> pattern, + final int userId, + final OnCheckCallback callback) { + AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() { + @Override + protected Boolean doInBackground(Void... args) { + return utils.checkPattern(pattern, userId); + } + + @Override + protected void onPostExecute(Boolean result) { + callback.onChecked(result); + } + }; + task.execute(); + return task; + } + + /** + * Verify a password asynchronously. + * + * @param utils The LockPatternUtils instance to use. + * @param password The password to check. + * @param challenge The challenge to verify against the pattern. + * @param userId The user to check against the pattern. + * @param callback The callback to be invoked with the verification result. + */ + public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils, + final String password, + final long challenge, + final int userId, + final OnVerifyCallback callback) { + AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() { + @Override + protected byte[] doInBackground(Void... args) { + return utils.verifyPassword(password, challenge, userId); + } + + @Override + protected void onPostExecute(byte[] result) { + callback.onVerified(result); + } + }; + task.execute(); + return task; + } + + /** + * Checks a password asynchronously. + * + * @param utils The LockPatternUtils instance to use. + * @param password The password to check. + * @param userId The user to check against the pattern. + * @param callback The callback to be invoked with the check result. + */ + public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils, + final String password, + final int userId, + final OnCheckCallback callback) { + AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() { + @Override + protected Boolean doInBackground(Void... args) { + return utils.checkPassword(password, userId); + } + + @Override + protected void onPostExecute(Boolean result) { + callback.onChecked(result); + } + }; + task.execute(); + return task; + } +} diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 3d23986..e5ef60c 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -1097,8 +1097,11 @@ public class LockPatternUtils { Log.w(TAG, "Only device owner may call setCredentialRequiredForDecrypt()"); return; } - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, required ? 1 : 0); + + if (isDeviceEncryptionEnabled()){ + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, required ? 1 : 0); + } } private boolean isDoNotAskCredentialsOnBootSet() { diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 01e835b..be727f1 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -661,13 +661,20 @@ public class ResolverDrawerLayout extends ViewGroup { } } + final int oldCollapsibleHeight = mCollapsibleHeight; mCollapsibleHeight = Math.max(0, heightUsed - alwaysShowHeight - getMaxCollapsedHeight()); mUncollapsibleHeight = heightUsed - mCollapsibleHeight; if (isLaidOut()) { final boolean isCollapsedOld = mCollapseOffset != 0; - mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); + if (oldCollapsibleHeight < mCollapsibleHeight + && mCollapseOffset == oldCollapsibleHeight) { + // Stay closed even at the new height. + mCollapseOffset = mCollapsibleHeight; + } else { + mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); + } final boolean isCollapsedNew = mCollapseOffset != 0; if (isCollapsedOld != isCollapsedNew) { notifyViewAccessibilityStateChangedIfNeeded( diff --git a/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java b/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java index 4d53d40..250932b 100644 --- a/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java +++ b/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java @@ -329,6 +329,14 @@ public class SSLSocketFactory implements LayeredSocketFactory { sslsock.setSoTimeout(soTimeout); try { + // BEGIN android-added + /* + * Make sure we have started the handshake before verifying. + * Otherwise when we go to the hostname verifier, it directly calls + * SSLSocket#getSession() which swallows SSL handshake errors. + */ + sslsock.startHandshake(); + // END android-added hostnameVerifier.verify(host, sslsock); // verifyHostName() didn't blowup - good! } catch (IOException iox) { @@ -389,6 +397,14 @@ public class SSLSocketFactory implements LayeredSocketFactory { port, autoClose ); + // BEGIN android-added + /* + * Make sure we have started the handshake before verifying. + * Otherwise when we go to the hostname verifier, it directly calls + * SSLSocket#getSession() which swallows SSL handshake errors. + */ + sslSocket.startHandshake(); + // END android-added hostnameVerifier.verify(host, sslSocket); // verifyHostName() didn't blowup - good! return sslSocket; |
